Skip to content

Commit

Permalink
docs: Adds a page to help find v9 icons for v0 migration (#25185)
Browse files Browse the repository at this point in the history
* docs: Adds a page to help find v9 icons for v0 migration

Uses the icon mapping in
https://github.com/YuanboXue-Amber/style-transform-tool/blob/master/apps/web/src/lib/iconMapping.json
by @YuanboXue to create migration documentation for v0 icons.

* virtualize

* make list longer

* fix types

* pr suggestions

* link to officla icon docs

* revert peresona
  • Loading branch information
ling1726 authored Oct 19, 2022
1 parent 099d9f0 commit 61a6356
Show file tree
Hide file tree
Showing 8 changed files with 2,426 additions and 0 deletions.
3 changes: 3 additions & 0 deletions apps/public-docsite-v9/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
},
"dependencies": {
"@fluentui/react": "^8.98.3",
"@fluentui/react-northstar": "^0.64.0",
"@fluentui/react-icons-northstar": "^0.64.0",
"@fluentui/scripts": "^1.0.0",
"@fluentui/storybook": "^1.0.0",
"@fluentui/react-components": "^9.5.1",
Expand All @@ -30,6 +32,7 @@
"@griffel/react": "^1.4.1",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-window": "^1.8.6",
"tslib": "^2.1.0"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import * as React from 'react';
import { DismissRegular, InfoRegular } from '@fluentui/react-icons';
import { makeStyles, shorthands, tokens, Button, mergeClasses, Tooltip } from '@fluentui/react-components';
import { V0IconComponent, V9IconComponent } from './types';

const useIconTileStyles = makeStyles({
root: {
position: 'relative',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
...shorthands.padding('5px', '0px'),
...shorthands.gap('10px'),
...shorthands.border(tokens.strokeWidthThin, 'solid', tokens.colorNeutralStroke1),
maxHeight: '105px',
':hover': {
backgroundColor: tokens.colorNeutralBackground2Hover,
},
},

badge: {
position: 'absolute',
top: '5px',
right: '10px',
},

warning: {
color: tokens.colorPaletteDarkOrangeBackground3,
},

success: {
color: tokens.colorPaletteGreenBackground3,
},

v0: {
width: '16px',
height: '16px',
},

v9: {
width: '20px',
height: '20px',
},

tile: {
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
...shorthands.gap('2px'),
},

buttonReset: {
resize: 'horizontal',
boxSizing: 'content-box',
backgroundColor: 'inherit',
color: 'inherit',
fontFamily: 'inherit',
fontSize: 'inherit',
lineHeight: 'normal',
...shorthands.overflow('visible'),
...shorthands.padding(0),
...shorthands.borderStyle('none'),
WebkitAppearance: 'button',
textAlign: 'unset',
},
});

interface IComparisonTileProps {
V0Icon: V0IconComponent;
V9Icon?: V9IconComponent;
}

export const ComparisonTile: React.FC<IComparisonTileProps> = ({ V0Icon, V9Icon }) => {
const noV9Icon = !V9Icon;

const tooltipWarningContent = <ul>{noV9Icon && <li>No equivalent icon available</li>}</ul>;

const styles = useIconTileStyles();
return (
<div className={styles.root} role="listitem">
<Tooltip relationship="description" content={noV9Icon ? tooltipWarningContent : 'Good to go!'}>
<Button
appearance="subtle"
className={mergeClasses(styles.badge, noV9Icon ? styles.warning : styles.success)}
icon={<InfoRegular />}
shape="circular"
/>
</Tooltip>
<div className={styles.tile}>
<div className={styles.v0}>{<V0Icon outline />}</div>
<div>{V0Icon.displayName}</div>
</div>
<div className={styles.tile}>
<div className={styles.v9}>
{V9Icon ? <V9Icon fontSize={20} /> : <DismissRegular color="red" fontSize={21} />}
</div>
<div>{V9Icon ? V9Icon.displayName : 'Not Available'}</div>
</div>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import * as React from 'react';
import { Provider, teamsV2Theme } from '@fluentui/react-northstar';
import * as v0Icons from '@fluentui/react-icons-northstar';
import * as v9Icons from '@fluentui/react-icons';
import { makeStyles, Input, Switch, Label } from '@fluentui/react-components';
import type { InputProps, SwitchProps } from '@fluentui/react-components';
import { iconMapping as rawMapping } from './iconMapping';
import { IconGrid } from './IconGrid';
import { useDebounce } from './useDebounce';
import { V0IconComponent, V9IconComponent } from './types';

const useStyles = makeStyles({
searchPanel: {
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
marginBottom: '10px',
marginTop: '20px',
},

switch: {
display: 'flex',
alignItems: 'center',
},
});

const _mapping = rawMapping
.map(entry => {
const v0IconName = `${entry.v0}`;
const v9IconName = `${entry.v9}Regular`;
const V0Icon = ((v0Icons as unknown) as Record<string, V0IconComponent>)[v0IconName];
const V9Icon = ((v9Icons as unknown) as Record<string, V9IconComponent | undefined>)[v9IconName];

if (!V0Icon) {
return null;
}

return {
v9Search: entry.v9?.toLowerCase(),
v0Search: entry.v0.toLowerCase(),
V0Icon,
V9Icon,
};
})
.filter(Boolean);
const mapping = _mapping.filter(Boolean) as Array<NonNullable<typeof _mapping[number]>>;

const IconCatalogInner: React.FC = () => {
const styles = useStyles();
const [searchTerm, setSearchTerm] = React.useState<string | undefined>(undefined);
const [searchV0, setSearchV0] = React.useState(true);

const updateSearch = React.useCallback(
(newSearchTerm: string) => {
setSearchTerm(newSearchTerm.toLowerCase());
},
[setSearchTerm],
);

const updateSearchDebounced = useDebounce(updateSearch, 220);

const entries = React.useMemo(
() =>
mapping.filter(entry => {
if (!searchTerm) {
return true;
}

if (searchV0) {
return entry.v0Search.includes(searchTerm);
}

return entry.v9Search?.includes(searchTerm);
}),
[searchTerm, searchV0],
);

const onInputChange: InputProps['onChange'] = React.useCallback((e, { value }) => updateSearchDebounced(value), [
updateSearchDebounced,
]);
const onSwitchChange: SwitchProps['onChange'] = React.useCallback((e, { checked }) => setSearchV0(checked), []);

return (
<>
<div>
<div className={styles.searchPanel}>
<Input
aria-label={`Search across ${mapping.length} icons`}
placeholder={`Search across ${mapping.length} icons`}
onChange={onInputChange}
/>
<div className={styles.switch}>
<Label htmlFor="searchToggle">{searchV0 ? 'Searching Northstar icons ' : 'Searching V9 icons'}</Label>
<Switch id="searchToggle" checked={searchV0} onChange={onSwitchChange} />
</div>
</div>
</div>
<div role="status">{entries.length} icon entries</div>
<IconGrid entries={entries} />
</>
);
};

// Simply hoists the northstar provider above the catalog to avoid unnecessarily
// re-rendering the provider.
const northstarTheme = { ...teamsV2Theme, staticStyles: [] };
export const IconCatalog: React.FC = () => (
<Provider theme={northstarTheme}>
<IconCatalogInner />
</Provider>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import * as React from 'react';
import { FixedSizeList as List } from 'react-window';
import type { ListChildComponentProps } from 'react-window';
import { ComparisonTile as ComparisonTileBase } from './ComparisonTile';
import { V0IconComponent, V9IconComponent } from './types';

const ComparisonTile = React.memo(ComparisonTileBase);

interface IIconGridProps {
entries: {
V0Icon: V0IconComponent;
V9Icon?: V9IconComponent;
}[];
}

const ROW_SIZE = 3;

interface IRowProps extends ListChildComponentProps {
data: IIconGridProps['entries'];
}

const Row = ({ index, style, data }: IRowProps) => {
const start = index * ROW_SIZE;
const items = data.slice(start, start + 3);
return (
<div style={style}>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 10 }}>
{items.map(({ V0Icon, V9Icon }) => (
<ComparisonTile key={V0Icon.displayName} V0Icon={V0Icon} V9Icon={V9Icon} />
))}
</div>
</div>
);
};

export const IconGrid: React.FC<IIconGridProps> = ({ entries }) => {
return (
<List width="100%" itemCount={entries.length / ROW_SIZE} height={600} itemData={entries} itemSize={110}>
{Row}
</List>
);
};
Loading

0 comments on commit 61a6356

Please sign in to comment.