feat(docs): In the Wild page with YAML data and AntD components (#36386)

Co-authored-by: Catherine Qu <catherine.qu@mail.utoronto.ca>
Co-authored-by: Evan Rusackas <evan@rusackas.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Catherine Qu
2025-12-17 12:42:29 -05:00
committed by GitHub
parent c9ec173647
commit 5ce4c52cfa
11 changed files with 1018 additions and 4 deletions

View File

@@ -0,0 +1,165 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import Layout from '@theme/Layout';
import { Avatar, Card, Col, Collapse, Row, Typography } from 'antd';
import BlurredSection from '../components/BlurredSection';
import SectionHeader from '../components/SectionHeader';
import DataSet from '../../../RESOURCES/INTHEWILD.yaml';
const { Text, Link } = Typography;
interface Organization {
name: string;
url: string;
logo?: string;
contributors?: string[];
}
interface DataSetType {
categories: Record<string, Organization[]>;
}
const typedDataSet = DataSet as DataSetType;
const ContributorAvatars = ({ contributors }: { contributors?: string[] }) => {
if (!contributors?.length) return null;
return (
<Avatar.Group size="small" max={{ count: 3 }}>
{contributors.map((handle) => {
const username = handle.replace('@', '');
return (
<a
key={username}
href={`https://github.com/${username}`}
target="_blank"
rel="noreferrer"
onClick={(e) => e.stopPropagation()}
>
<Avatar
src={`https://github.com/${username}.png?size=40`}
alt={username}
style={{ cursor: 'pointer' }}
>
{username.charAt(0).toUpperCase()}
</Avatar>
</a>
);
})}
</Avatar.Group>
);
};
export default function InTheWild() {
return (
<Layout title="In the Wild" description="Organizations using Apache Superset">
<main>
<BlurredSection>
<SectionHeader
level="h2"
title="In the Wild"
subtitle="See who's using Superset and join our growing community"
/>
<div style={{ textAlign: 'center', marginTop: 10 }}>
<Link
href="https://github.com/apache/superset/edit/master/RESOURCES/INTHEWILD.yaml"
target="_blank"
>
Add your name/org!
</Link>
</div>
</BlurredSection>
<div style={{ maxWidth: 850, margin: '70px auto 60px', padding: '0 20px' }}>
<Collapse
bordered={false}
defaultActiveKey={Object.keys(typedDataSet.categories)}
style={{
background: 'var(--ifm-background-color)',
border: '1px solid var(--ifm-border-color)',
borderRadius: 10,
}}
items={Object.entries(typedDataSet.categories).map(([category, items]) => {
const logoItems = items.filter(({ logo }) => logo?.trim());
const textItems = items.filter(({ logo }) => !logo?.trim());
return {
key: category,
label: (
<Text strong style={{ fontSize: 16, lineHeight: '22px' }}>
{category} ({items.length})
</Text>
),
children: (
<>
{logoItems.length > 0 && (
<Row gutter={[16, 16]} style={{ marginBottom: textItems.length > 0 ? 24 : 0 }}>
{logoItems.map(({ name, url, logo, contributors }) => (
<Col xs={24} sm={12} md={8} key={name}>
<a href={url} target="_blank" rel="noreferrer">
<Card
hoverable
style={{ height: 150, position: 'relative' }}
styles={{ body: { padding: 16, height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center' } }}
>
<img
src={`/img/logos/${logo}`}
alt={name}
style={{ maxHeight: 80, maxWidth: '100%', objectFit: 'contain' }}
/>
{contributors?.length && (
<div style={{ position: 'absolute', bottom: 8, right: 8 }}>
<ContributorAvatars contributors={contributors} />
</div>
)}
</Card>
</a>
</Col>
))}
</Row>
)}
{textItems.length > 0 && (
<Row gutter={[8, 8]}>
{textItems.map(({ name, url, contributors }) => (
<Col xs={24} sm={12} md={8} key={name}>
<a href={url} target="_blank" rel="noreferrer">
<Card
size="small"
hoverable
styles={{ body: { padding: '8px 12px', display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8 } }}
>
<Text ellipsis style={{ flex: 1 }}>{name}</Text>
<ContributorAvatars contributors={contributors} />
</Card>
</a>
</Col>
))}
</Row>
)}
</>
),
};
})}
/>
</div>
</main>
</Layout>
);
}

View File

@@ -19,15 +19,43 @@
import { useRef, useState, useEffect, JSX } from 'react';
import Layout from '@theme/Layout';
import Link from '@docusaurus/Link';
import { Carousel } from 'antd';
import { Card, Carousel, Flex } from 'antd';
import styled from '@emotion/styled';
import GitHubButton from 'react-github-btn';
import { mq } from '../utils';
import { Databases } from '../resources/data';
import SectionHeader from '../components/SectionHeader';
import BlurredSection from '../components/BlurredSection';
import DataSet from '../../../RESOURCES/INTHEWILD.yaml';
import '../styles/main.less';
interface Organization {
name: string;
url: string;
logo?: string;
}
interface DataSetType {
categories: Record<string, Organization[]>;
}
const typedDataSet = DataSet as DataSetType;
// Extract all organizations with logos for the carousel
const companiesWithLogos = Object.values(typedDataSet.categories)
.flat()
.filter((org) => org.logo?.trim());
// Fisher-Yates shuffle for fair randomization
function shuffleArray<T>(array: T[]): T[] {
const shuffled = [...array];
for (let i = shuffled.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
}
return shuffled;
}
const features = [
{
image: 'powerful-yet-easy.jpg',
@@ -452,6 +480,7 @@ export default function Home(): JSX.Element {
const slider = useRef(null);
const [slideIndex, setSlideIndex] = useState(0);
const [shuffledCompanies, setShuffledCompanies] = useState(companiesWithLogos);
const onChange = (current, next) => {
setSlideIndex(next);
@@ -479,6 +508,11 @@ export default function Home(): JSX.Element {
}
};
// Shuffle companies on mount for fair rotation
useEffect(() => {
setShuffledCompanies(shuffleArray(companiesWithLogos));
}, []);
// Set up dark <-> light navbar change
useEffect(() => {
changeToDark();
@@ -747,6 +781,74 @@ export default function Home(): JSX.Element {
</span>
</StyledIntegrations>
</BlurredSection>
{/* Only show carousel when we have enough logos (>10) for a good display */}
{companiesWithLogos.length > 10 && (
<BlurredSection>
<div style={{ padding: '0 20px' }}>
<SectionHeader
level="h2"
title="Trusted by teams everywhere"
subtitle="Join thousands of companies using Superset to explore and visualize their data"
/>
<div style={{ maxWidth: 1160, margin: '25px auto 0' }}>
<Carousel
autoplay
autoplaySpeed={2000}
slidesToShow={6}
slidesToScroll={1}
dots={false}
responsive={[
{ breakpoint: 1024, settings: { slidesToShow: 4 } },
{ breakpoint: 768, settings: { slidesToShow: 3 } },
{ breakpoint: 480, settings: { slidesToShow: 2 } },
]}
>
{shuffledCompanies.map(({ name, url, logo }) => (
<div key={name}>
<a
href={url}
target="_blank"
rel="noreferrer"
aria-label={`Visit ${name}`}
>
<Card
style={{ margin: '0 8px' }}
styles={{
body: {
height: 80,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
padding: 16,
},
}}
>
<img
src={`/img/logos/${logo}`}
alt={name}
title={name}
style={{ maxHeight: 48, maxWidth: '100%', objectFit: 'contain' }}
/>
</Card>
</a>
</div>
))}
</Carousel>
</div>
<Flex justify="center" style={{ marginTop: 30, fontSize: 17 }}>
<Link to="/inTheWild">See all companies</Link>
<span style={{ margin: '0 8px' }}>·</span>
<a
href="https://github.com/apache/superset/edit/master/RESOURCES/INTHEWILD.yaml"
target="_blank"
rel="noreferrer"
>
Add yours to the list!
</a>
</Flex>
</div>
</BlurredSection>
)}
</StyledMain>
</Layout>
);

28
docs/src/types/yaml.d.ts vendored Normal file
View File

@@ -0,0 +1,28 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
declare module '*.yaml' {
const content: unknown;
export default content;
}
declare module '*.yml' {
const content: unknown;
export default content;
}

View File

@@ -25,6 +25,13 @@ export default function webpackExtendPlugin(): Plugin<void> {
name: 'custom-webpack-plugin',
configureWebpack(config) {
const isDev = process.env.NODE_ENV === 'development';
// Add YAML loader rule directly to existing rules
config.module?.rules?.push({
test: /\.ya?ml$/,
use: 'js-yaml-loader',
});
return {
devtool: isDev ? 'eval-source-map' : config.devtool,
...(isDev && {