Projects STRLCPY gradejs Commits ab0b7d67
🤬
  • wip: replace old homepage with new one, hardcode number of initial number of entities shown for keywords and people, minor typing/styling/design fixes

  • Loading...
  • Dmitry Shakun committed 2 years ago
    ab0b7d67
    1 parent 12880863
  • ■ ■ ■ ■ ■ ■
    packages/web/src/components/layouts/Home/Home.module.scss
    1  -@import '~styles/responsive.scss';
    2  - 
    3  -.wrapper {
    4  - height: 100%;
    5  - display: flex;
    6  - flex-direction: column;
    7  - justify-content: space-between;
    8  - align-items: center;
    9  -}
    10  - 
    11  -.content {
    12  - flex: 1;
    13  - display: flex;
    14  - flex-direction: column;
    15  - justify-content: center;
    16  -}
    17  - 
    18  -.address {
    19  - width: 496px;
    20  - max-width: 100%;
    21  - margin: 0 16px 16px 0;
    22  - box-sizing: border-box;
    23  - 
    24  - @include mobile {
    25  - margin-right: 0;
    26  - }
    27  -}
    28  - 
    29  -.submit {
    30  - width: 218px;
    31  - 
    32  - @include mobile {
    33  - width: 100%;
    34  - }
    35  -}
    36  - 
    37  -.disclaimer {
    38  - padding: 0;
    39  - margin: 8px 0 0;
    40  - font-size: 14px;
    41  - line-height: 19px;
    42  - color: #a5a5a5;
    43  - max-width: 60%;
    44  - min-height: 38px;
    45  - 
    46  - @include mobile {
    47  - max-width: 100%;
    48  - margin-top: 24px;
    49  - }
    50  -}
    51  - 
    52  -.learnMore {
    53  - color: #000;
    54  -}
    55  - 
    56  -.error {
    57  - color: #ff3b30;
    58  -}
    59  - 
  • ■ ■ ■ ■ ■
    packages/web/src/components/layouts/Home/Home.stories.tsx
    1 1  import React from 'react';
    2  -import Home, { Props } from './Home';
     2 +import Home from './Home';
     3 +import { ComponentMeta, ComponentStory } from '@storybook/react';
    3 4   
    4 5  export default {
    5 6   title: 'Layouts / Home',
    skipped 1 lines
    7 8   parameters: {
    8 9   layout: 'fullscreen',
    9 10   },
    10  -};
     11 +} as ComponentMeta<typeof Home>;
    11 12   
    12  -export const Default = (args: Props) => <Home {...args} />;
     13 +const Template: ComponentStory<typeof Home> = (args) => <Home {...args} />;
     14 + 
     15 +export const Default = Template.bind({});
     16 +Default.args = {
     17 + suggestions: ['tinkoff.ru', 'pinterest.com', 'github.com', 'gradejs.com', 'npmjs.com'],
     18 +};
    13 19   
  • ■ ■ ■ ■ ■ ■
    packages/web/src/components/layouts/Home/Home.tsx
    1  -/* eslint-disable react/button-has-type */
    2  -import React, { useCallback } from 'react';
    3  -import clsx from 'clsx';
    4  -import { SubmitHandler, useForm } from 'react-hook-form';
    5  -import { Button, Header, Section, TextInput, Wrapper } from 'components/ui';
    6  -import styles from './Home.module.scss';
     1 +import React from 'react';
     2 +import { CardProps } from '../../ui/Card/Card';
     3 +import Hero from '../../ui/Hero/Hero';
     4 +import Container from 'components/ui/Container/Container';
     5 +import CardList from '../../ui/CardList/CardList';
     6 +import CardGroups from '../../ui/CardGroups/CardGroups';
     7 +import Footer from '../../ui/Footer/Footer';
     8 +import CardGroup from '../../ui/CardGroup/CardGroup';
    7 9   
    8  -type FormData = {
    9  - address: string;
     10 +type Props = {
     11 + suggestions: string[];
    10 12  };
    11 13   
    12  -export type Props = {
    13  - onSubmit: SubmitHandler<FormData>;
    14  - isLoading?: boolean;
    15  -};
     14 +export default function Home({ suggestions }: Props) {
     15 + // TODO: mock data, remove later
     16 + const popularCards: CardProps[] = [
     17 + {
     18 + id: 'uExBVGuF',
     19 + title: 'github.com',
     20 + icon: 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
     21 + packageTags: {
     22 + featuredPackages: ['mdast-util-from-markdown', 'react', 'react-dom'],
     23 + restPackages: 45,
     24 + },
     25 + },
     26 + {
     27 + id: '1EkL1u5g',
     28 + title: 'fingerprint.com',
     29 + icon: 'https://avatars.githubusercontent.com/u/67208791?s=200&v=4',
     30 + packageTags: {
     31 + featuredPackages: ['mdast-util-from-markdown', 'react', 'react-dom'],
     32 + restPackages: 45,
     33 + },
     34 + },
     35 + {
     36 + id: 'mhwO2bPM',
     37 + title: 'facebook.com',
     38 + icon: 'https://avatars.githubusercontent.com/u/69631?s=200&v=4',
     39 + packageTags: {
     40 + featuredPackages: ['react'],
     41 + restPackages: 45,
     42 + },
     43 + },
     44 + ];
    16 45   
    17  -export default function Home({ onSubmit, isLoading }: Props) {
    18  - const {
    19  - register,
    20  - handleSubmit,
    21  - formState: { errors },
    22  - setError,
    23  - } = useForm<FormData>();
     46 + // TODO: mock data, remove later
     47 + const popularPackages: CardProps[] = [
     48 + {
     49 + id: 'FPsBcl8R',
     50 + title: '@team-griffin/react-heading-section',
     51 + description: "This package's job is to automatically determine...",
     52 + featuredSites: {
     53 + iconList: [
     54 + 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
     55 + 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
     56 + 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
     57 + ],
     58 + numberOfUses: 5265,
     59 + },
     60 + },
     61 + {
     62 + id: 'emtYcsUh',
     63 + title: 'unist-util-generated',
     64 + description: 'unist utility to check if a node is generated',
     65 + featuredSites: {
     66 + iconList: [
     67 + 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
     68 + 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
     69 + 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
     70 + ],
     71 + numberOfUses: 5265,
     72 + },
     73 + },
     74 + {
     75 + id: 'TYIwvAfy',
     76 + title: 'react-smooth',
     77 + description: 'is a animation library work on React',
     78 + featuredSites: {
     79 + iconList: [
     80 + 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
     81 + 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
     82 + 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
     83 + ],
     84 + numberOfUses: 5265,
     85 + },
     86 + },
     87 + {
     88 + id: 'Lq1pEEX7',
     89 + title: 'unist-util-position',
     90 + description: 'unist utility to get the positional info of nodes',
     91 + featuredSites: {
     92 + iconList: [
     93 + 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
     94 + 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
     95 + 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
     96 + ],
     97 + numberOfUses: 5265,
     98 + },
     99 + },
     100 + {
     101 + id: 'cWOgIbmp',
     102 + title: 'vfile-message',
     103 + description: 'Create vfile messages',
     104 + featuredSites: {
     105 + iconList: [
     106 + 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
     107 + 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
     108 + 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
     109 + ],
     110 + numberOfUses: 5265,
     111 + },
     112 + },
     113 + {
     114 + id: 'UT97Vpoi',
     115 + title: 'Go to all Popular packages',
     116 + variant: 'toAll',
     117 + },
     118 + ];
    24 119   
    25  - const validate = useCallback(
    26  - (data: FormData) => {
    27  - let { address } = data;
     120 + // TODO: mock data, remove later
     121 + const vulnerablePackages: CardProps[] = [
     122 + {
     123 + id: 'LnO9Xynn',
     124 + title: 'disneyland.omsk.ru/signup',
     125 + vulnerablePackage: {
     126 + name: 'mdast-util-from-markdown',
     127 + },
     128 + variant: 'vulnerable',
     129 + },
     130 + {
     131 + id: '-A74UAy8',
     132 + title: 'disneyland.omsk.ru/signup',
     133 + vulnerablePackage: {
     134 + name: 'mdast-util-from-markdown',
     135 + moreCount: 1,
     136 + },
     137 + variant: 'vulnerable',
     138 + },
     139 + {
     140 + id: 'DPa05I2W',
     141 + title: 'disneyland.omsk.ru/signup',
     142 + vulnerablePackage: {
     143 + name: 'mdast-util-from-markdown',
     144 + },
     145 + variant: 'vulnerable',
     146 + },
     147 + ];
    28 148   
    29  - if (!address.startsWith('http')) {
    30  - address = `https://${address}`;
    31  - }
     149 + return (
     150 + <>
     151 + <Hero suggestions={suggestions} />
    32 152   
    33  - try {
    34  - address = new URL(address).toString(); // normalize
    35  - onSubmit({ address });
    36  - } catch (e) {
    37  - setError('address', { message: 'Invalid origin format. Should be: https://example.com' });
    38  - }
     153 + <Container>
     154 + <CardGroups>
     155 + <CardGroup title='Popular search queries'>
     156 + <CardList cards={popularCards} />
     157 + </CardGroup>
    39 158   
    40  - // if (address.match(re)) {
    41  - // } else {
    42  - // setError('address', { message: 'Invalid origin format. Should be: https://example.com' });
    43  - // }
    44  - },
    45  - [setError]
    46  - );
     159 + <CardGroup title='Popular packages'>
     160 + <CardList cards={popularPackages} />
     161 + </CardGroup>
    47 162   
    48  - const { address: error } = errors;
     163 + <CardGroup title='Vulnerable sites'>
     164 + <CardList cards={vulnerablePackages} variant='vertical' />
     165 + </CardGroup>
    49 166   
    50  - return (
    51  - <Wrapper className={styles.wrapper}>
    52  - <Header />
    53  - <Section className={styles.content}>
    54  - <h1>Analyze webpack production bundle</h1>
    55  - <form onSubmit={handleSubmit(validate)} noValidate>
    56  - <TextInput
    57  - type='url'
    58  - className={styles.address}
    59  - placeholder='Enter a website URL'
    60  - name='address'
    61  - register={register}
    62  - error={errors.address}
    63  - disabled={isLoading}
    64  - />
    65  - <Button className={styles.submit} variant='action' type='submit' disabled={isLoading}>
    66  - Start
    67  - </Button>
     167 + <CardGroup title='Authors of popular packages'>
     168 + <CardList cards={popularCards} />
     169 + </CardGroup>
     170 + </CardGroups>
     171 + </Container>
    68 172   
    69  - {error?.message ? (
    70  - <p className={clsx(styles.disclaimer, styles.error)}>{error.message}</p>
    71  - ) : (
    72  - <p className={clsx(styles.disclaimer)}>
    73  - GradeJS will analyze production JavaScript files and match webpack bundled modules to
    74  - 1,826 indexed NPM libraries over 54,735 releases.{' '}
    75  - <a
    76  - href='https://github.com/gradejs/gradejs/discussions/6'
    77  - target='_blank'
    78  - rel='noreferrer'
    79  - className={clsx(styles.learnMore)}
    80  - >
    81  - Learn more
    82  - </a>
    83  - </p>
    84  - )}
    85  - </form>
    86  - </Section>
    87  - </Wrapper>
     173 + <Footer />
     174 + </>
    88 175   );
    89 176  }
    90 177   
  • ■ ■ ■ ■ ■ ■
    packages/web/src/components/layouts/HomeNew/HomeNew.stories.tsx
    1  -import React from 'react';
    2  -import HomeNew from './HomeNew';
    3  -import { ComponentMeta, ComponentStory } from '@storybook/react';
    4  - 
    5  -export default {
    6  - title: 'Layouts / Home',
    7  - component: HomeNew,
    8  - parameters: {
    9  - layout: 'fullscreen',
    10  - },
    11  -} as ComponentMeta<typeof HomeNew>;
    12  - 
    13  -const Template: ComponentStory<typeof HomeNew> = (args) => <HomeNew {...args} />;
    14  - 
    15  -export const New = Template.bind({});
    16  -New.args = {
    17  - suggestions: ['tinkoff.ru', 'pinterest.com', 'github.com', 'gradejs.com', 'npmjs.com'],
    18  -};
    19  - 
  • ■ ■ ■ ■ ■ ■
    packages/web/src/components/layouts/HomeNew/HomeNew.tsx
    1  -import React from 'react';
    2  -import { CardProps } from '../../ui/Card/Card';
    3  -import Hero from '../../ui/Hero/Hero';
    4  -import Container from 'components/ui/Container/Container';
    5  -import CardList from '../../ui/CardList/CardList';
    6  -import CardGroups from '../../ui/CardGroups/CardGroups';
    7  -import Footer from '../../ui/Footer/Footer';
    8  -import CardGroup from '../../ui/CardGroup/CardGroup';
    9  - 
    10  -type Props = {
    11  - suggestions: string[];
    12  -};
    13  - 
    14  -export default function HomeNew({ suggestions }: Props) {
    15  - // TODO: mock date, remove later
    16  - const popularCards: CardProps[] = [
    17  - {
    18  - id: 'uExBVGuF',
    19  - title: 'github.com',
    20  - icon: 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
    21  - packageTags: {
    22  - featuredPackages: ['mdast-util-from-markdown', 'react', 'react-dom'],
    23  - restPackages: 45,
    24  - },
    25  - },
    26  - {
    27  - id: '1EkL1u5g',
    28  - title: 'fingerprint.com',
    29  - icon: 'https://avatars.githubusercontent.com/u/67208791?s=200&v=4',
    30  - packageTags: {
    31  - featuredPackages: ['mdast-util-from-markdown', 'react', 'react-dom'],
    32  - restPackages: 45,
    33  - },
    34  - },
    35  - {
    36  - id: 'mhwO2bPM',
    37  - title: 'facebook.com',
    38  - icon: 'https://avatars.githubusercontent.com/u/69631?s=200&v=4',
    39  - packageTags: {
    40  - featuredPackages: ['react'],
    41  - restPackages: 45,
    42  - },
    43  - },
    44  - ];
    45  - 
    46  - // TODO: mock date, remove later
    47  - const popularPackages: CardProps[] = [
    48  - {
    49  - id: 'FPsBcl8R',
    50  - title: '@team-griffin/react-heading-section',
    51  - description: "This package's job is to automatically determine...",
    52  - featuredSites: {
    53  - iconList: [
    54  - 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
    55  - 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
    56  - 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
    57  - ],
    58  - numberOfUses: 5265,
    59  - },
    60  - },
    61  - {
    62  - id: 'emtYcsUh',
    63  - title: 'unist-util-generated',
    64  - description: 'unist utility to check if a node is generated',
    65  - featuredSites: {
    66  - iconList: [
    67  - 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
    68  - 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
    69  - 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
    70  - ],
    71  - numberOfUses: 5265,
    72  - },
    73  - },
    74  - {
    75  - id: 'TYIwvAfy',
    76  - title: 'react-smooth',
    77  - description: 'is a animation library work on React',
    78  - featuredSites: {
    79  - iconList: [
    80  - 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
    81  - 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
    82  - 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
    83  - ],
    84  - numberOfUses: 5265,
    85  - },
    86  - },
    87  - {
    88  - id: 'Lq1pEEX7',
    89  - title: 'unist-util-position',
    90  - description: 'unist utility to get the positional info of nodes',
    91  - featuredSites: {
    92  - iconList: [
    93  - 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
    94  - 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
    95  - 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
    96  - ],
    97  - numberOfUses: 5265,
    98  - },
    99  - },
    100  - {
    101  - id: 'cWOgIbmp',
    102  - title: 'vfile-message',
    103  - description: 'Create vfile messages',
    104  - featuredSites: {
    105  - iconList: [
    106  - 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
    107  - 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
    108  - 'https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg',
    109  - ],
    110  - numberOfUses: 5265,
    111  - },
    112  - },
    113  - {
    114  - id: 'UT97Vpoi',
    115  - title: 'Go to all Popular packages',
    116  - variant: 'toAll',
    117  - },
    118  - ];
    119  - 
    120  - // TODO: mock date, remove later
    121  - const vulnerablePackages: CardProps[] = [
    122  - {
    123  - id: 'LnO9Xynn',
    124  - title: 'disneyland.omsk.ru/signup',
    125  - vulnerablePackage: {
    126  - name: 'mdast-util-from-markdown',
    127  - },
    128  - variant: 'vulnerable',
    129  - },
    130  - {
    131  - id: '-A74UAy8',
    132  - title: 'disneyland.omsk.ru/signup',
    133  - vulnerablePackage: {
    134  - name: 'mdast-util-from-markdown',
    135  - moreCount: 1,
    136  - },
    137  - variant: 'vulnerable',
    138  - },
    139  - {
    140  - id: 'DPa05I2W',
    141  - title: 'disneyland.omsk.ru/signup',
    142  - vulnerablePackage: {
    143  - name: 'mdast-util-from-markdown',
    144  - },
    145  - variant: 'vulnerable',
    146  - },
    147  - ];
    148  - 
    149  - return (
    150  - <>
    151  - <Hero suggestions={suggestions} />
    152  - 
    153  - <Container>
    154  - <CardGroups>
    155  - <CardGroup title='Popular search queries'>
    156  - <CardList cards={popularCards} />
    157  - </CardGroup>
    158  - 
    159  - <CardGroup title='Popular packages'>
    160  - <CardList cards={popularPackages} />
    161  - </CardGroup>
    162  - 
    163  - <CardGroup title='Vulnerable sites'>
    164  - <CardList cards={vulnerablePackages} variant='vertical' />
    165  - </CardGroup>
    166  - 
    167  - <CardGroup title='Authors of popular packages'>
    168  - <CardList cards={popularCards} />
    169  - </CardGroup>
    170  - </CardGroups>
    171  - </Container>
    172  - 
    173  - <Footer />
    174  - </>
    175  - );
    176  -}
    177  - 
  • ■ ■ ■ ■ ■
    packages/web/src/components/layouts/SearchResults/SearchResults.module.scss
    skipped 36 lines
    37 37   grid-gap: 16px;
    38 38   align-items: start;
    39 39   grid-template-rows: min-content;
     40 + grid-template-columns: minmax(0, 1fr);
    40 41   
    41 42   @include mobile-and-tablet {
    42 43   grid-column: 1;
    skipped 58 lines
  • ■ ■ ■ ■ ■
    packages/web/src/components/layouts/SearchResults/SearchResults.tsx
    skipped 14 lines
    15 15  import { Button } from '../../ui';
    16 16   
    17 17  export default function SearchResults() {
    18  - // TODO: mock date, remove later
     18 + // TODO: mock data, remove later
    19 19   const similarCards: CardProps[] = [
    20 20   {
    21 21   id: 'uExBVGuF',
    skipped 24 lines
    46 46   },
    47 47   ];
    48 48   
    49  - // TODO: mock date, remove later
     49 + // TODO: mock data, remove later
    50 50   const popularPackages: CardProps[] = [
    51 51   {
    52 52   id: 'FPsBcl8R',
    skipped 67 lines
    120 120   },
    121 121   ];
    122 122   
    123  - const keyWords = {
    124  - fullList: [
    125  - {
    126  - id: '#art',
    127  - name: '#art',
    128  - },
    129  - {
    130  - id: '#angular',
    131  - name: '#angular',
    132  - },
    133  - {
    134  - id: '#moment',
    135  - name: '#moment',
    136  - },
    137  - {
    138  - id: '#date',
    139  - name: '#date',
    140  - },
    141  - {
    142  - id: '#react',
    143  - name: '#react',
    144  - },
    145  - {
    146  - id: '#parse',
    147  - name: '#parse',
    148  - },
    149  - {
    150  - id: '#fb',
    151  - name: '#fb',
    152  - },
    153  - ],
    154  - featuredItems: ['#moment', '#date', '#react', '#parse', '#fb'],
    155  - };
     123 + // TODO: mock data, remove later
     124 + const keyWords = ['#moment', '#date', '#react', '#parse', '#fb', '#angular', '#vue', '#ember'];
    156 125   
     126 + // TODO: mock data, remove later
    157 127   const vulnerabilities = ['Vulnerabilities', 'Outdated', 'Duplicate'];
    158 128   
    159  - const authors = {
    160  - fullList: [
    161  - {
    162  - id: 'acdlite',
    163  - name: 'acdlite',
    164  - },
    165  - {
    166  - id: 'gaearon',
    167  - name: 'gaearon',
    168  - },
    169  - {
    170  - id: 'sophiebits',
    171  - name: 'sophiebits',
    172  - },
    173  - {
    174  - id: 'trueadm',
    175  - name: 'trueadm',
    176  - },
    177  - ],
    178  - featuredItems: ['acdlite', 'gaearon', 'sophiebits', 'trueadm'],
    179  - };
     129 + // TODO: mock data, remove later
     130 + const authors = ['gaearon', 'acdlite', 'sophiebits', 'sebmarkbage', 'zpao', 'trueadm', 'bvaughn'];
    180 131   
    181  - const [selectedKeywords, setSelectedKeywords] = useState<string[] | []>([]);
    182  - const [selectedProblems, setSelectedProblems] = useState<string[] | []>([]);
    183  - const [selectedAuthors, setSelectedAuthors] = useState<string[] | []>([]);
     132 + const [selectedKeywords, setSelectedKeywords] = useState<string[]>([]);
     133 + const [selectedProblems, setSelectedProblems] = useState<string[]>([]);
     134 + const [selectedAuthors, setSelectedAuthors] = useState<string[]>([]);
    184 135   
    185 136   const handleFiltersChange = (
    186 137   name: string,
    187  - state: string[] | [],
     138 + state: string[],
    188 139   setState: React.SetStateAction<any>
    189 140   ) => {
    190 141   const temp = [...state];
    skipped 81 lines
    272 223   
    273 224   <div className={styles.sidebarItem}>
    274 225   <SidebarCategory
    275  - category={keyWords}
     226 + keywordsList={keyWords}
    276 227   selectedKeywords={selectedKeywords}
    277 228   selectHandler={handleKeywordsChange}
    278 229   renderComponent='chip'
    skipped 3 lines
    282 233   
    283 234   <div className={styles.sidebarItem}>
    284 235   <SidebarCategory
    285  - simpleCategory={vulnerabilities}
     236 + keywordsList={vulnerabilities}
    286 237   selectedKeywords={selectedProblems}
    287 238   selectHandler={handleProblemsChange}
    288 239   renderComponent='checkbox'
    skipped 2 lines
    291 242   
    292 243   <div className={styles.sidebarItem}>
    293 244   <SidebarCategory
    294  - category={authors}
     245 + keywordsList={authors}
    295 246   selectedKeywords={selectedAuthors}
    296 247   selectHandler={handleAuthorsChange}
    297 248   renderComponent='person'
    skipped 41 lines
  • ■ ■ ■ ■ ■ ■
    packages/web/src/components/ui/Chip/Chip.tsx
    skipped 5 lines
    6 6  export type ChipProps = {
    7 7   children: React.ReactNode;
    8 8   className?: string;
    9  - variant?: 'primary' | 'secondary' | 'outlined' | 'suggest' | string;
    10  - size?: 'default' | 'medium' | 'large' | string;
    11  - font?: 'sans-serif' | 'monospace' | string;
     9 + variant?: 'primary' | 'secondary' | 'outlined' | 'suggest';
     10 + size?: 'default' | 'medium' | 'large';
     11 + font?: 'sans-serif' | 'monospace';
    12 12   fontWeight?: 400 | 500;
    13 13   fontSize?: 'small' | 'regular';
    14 14   icon?: React.ReactElement<IconProps>;
    skipped 33 lines
  • ■ ■ ■ ■ ■ ■
    packages/web/src/components/ui/SidebarCategory/SidebarCategory.tsx
    skipped 7 lines
    8 8  import Checkbox from '../Checkbox/Checkbox';
    9 9  import SidebarCategorySearch from '../SidebarCategorySearch/SidebarCategorySearch';
    10 10   
    11  -type listItem = {
    12  - id: string;
    13  - name: string;
    14  -};
    15  - 
    16 11  type GroupItem = {
    17 12   group: string;
    18  - children: listItem[];
     13 + children: string[];
    19 14  };
    20 15   
    21 16  type Group = {
    skipped 1 lines
    23 18  };
    24 19   
    25 20  type Props = {
    26  - category?: {
    27  - fullList: listItem[];
    28  - featuredItems: string[];
    29  - };
    30  - simpleCategory?: string[];
     21 + keywordsList: string[];
    31 22   selectedKeywords: string[];
    32 23   selectHandler: (name: string) => void;
    33 24   renderComponent: 'chip' | 'checkbox' | 'person';
    skipped 1 lines
    35 26  };
    36 27   
    37 28  export default function SidebarCategory({
    38  - category,
    39  - simpleCategory,
     29 + keywordsList,
    40 30   selectedKeywords,
    41 31   selectHandler,
    42 32   renderComponent,
    skipped 1 lines
    44 34  }: Props) {
    45 35   const [open, setOpen] = useState<boolean>(false);
    46 36   const [searchValue, setSearchValue] = useState<string>('');
    47  - const [list, setList] = useState<GroupItem[] | []>([]);
     37 + const [list, setList] = useState<GroupItem[]>([]);
    48 38   
    49  - const sortAndGroupList = (unorderedList: listItem[], value: string): GroupItem[] => {
    50  - const filteredList = unorderedList.filter((item) => item.name.includes(value));
    51  - const sortedList = filteredList.sort((a: listItem, b: listItem) =>
    52  - a.name.localeCompare(b.name)
    53  - );
     39 + const sortAndGroupList = (unorderedList: string[], value: string): GroupItem[] => {
     40 + const filteredList = unorderedList.filter((item) => item.includes(value));
     41 + const sortedList = filteredList.sort((a: string, b: string) => a.localeCompare(b));
    54 42   
    55 43   const groups = sortedList.reduce((r: Group, e) => {
    56  - const group = e.name.includes('#') ? e.name[1] : e.name[0];
     44 + const group = e.includes('#') ? e[1] : e[0];
    57 45   if (!r[group]) r[group] = { group, children: [e] };
    58 46   else r[group].children.push(e);
    59 47   return r;
    skipped 15 lines
    75 63   };
    76 64   
    77 65   let combinedList, chips, checkboxes, people;
    78  - if (searchable && category) {
    79  - const { featuredItems, fullList } = category;
    80  - 
     66 + if (searchable) {
    81 67   // FIXME: not sure that this is optimal UX, because when we're selecting item from featured list,
    82 68   // it jumps to first half of the list with other previously selected items, maybe it's fine though
    83  - combinedList = fullList &&
    84  - featuredItems && [...new Set([...selectedKeywords, ...featuredItems])];
     69 + combinedList = [...new Set([...selectedKeywords, ...keywordsList])];
    85 70   
    86 71   useEffect(() => {
    87  - const filteredList = sortAndGroupList(fullList, searchValue);
     72 + const filteredList = sortAndGroupList(keywordsList, searchValue);
    88 73   setList(filteredList);
    89 74   }, [searchValue]);
    90 75   
    91  - chips = combinedList?.map((chip) => (
     76 + // Show only first 6 element from list
     77 + chips = combinedList.slice(0, 6).map((chip) => (
    92 78   <Chip
    93 79   key={chip}
    94 80   className={clsx(
    skipped 8 lines
    103 89   </Chip>
    104 90   ));
    105 91   
     92 + // Show only first 4 element from list
    106 93   people = (
    107 94   <div className={styles.authors}>
    108  - {combinedList?.map((person) => (
     95 + {combinedList.slice(0, 4).map((person) => (
    109 96   <Person
    110 97   key={person}
    111 98   name={person}
    skipped 7 lines
    119 106   } else {
    120 107   checkboxes = (
    121 108   <div className={styles.checkboxGroup}>
    122  - {simpleCategory?.map((name) => (
     109 + {keywordsList?.map((name) => (
    123 110   <Checkbox
    124 111   key={name}
    125 112   label={name}
    skipped 45 lines
    171 158   renderComponent={renderComponent}
    172 159   clearInput={clearInput}
    173 160   selectedItems={selectedKeywords}
    174  - list={list}
     161 + alphabeticalGroups={list}
    175 162   />
    176 163   ) : (
    177 164   renderedList
    skipped 11 lines
  • ■ ■ ■ ■ ■ ■
    packages/web/src/components/ui/SidebarCategorySearch/SidebarCategorySearch.tsx
    skipped 2 lines
    3 3  import { Icon } from '../Icon/Icon';
    4 4  import clsx from 'clsx';
    5 5   
    6  -type listItem = {
    7  - id: string;
    8  - name: string;
     6 +type GroupItem = {
     7 + group: string;
     8 + children: string[];
    9 9  };
    10 10   
    11  -type GroupItem = {
    12  - group: string;
    13  - children: listItem[];
     11 +type SearchItem = {
     12 + item: string;
     13 + selectedItems: string[];
     14 + selectHandler: (name: string) => void;
     15 + renderComponent: string;
    14 16  };
    15 17   
     18 +function SearchItem({ item, selectedItems, selectHandler, renderComponent }: SearchItem) {
     19 + return (
     20 + <div
     21 + className={clsx(styles.groupItem, selectedItems.includes(item) && styles.groupItemActive)}
     22 + onClick={() => selectHandler(item)}
     23 + >
     24 + {renderComponent === 'person' && (
     25 + // TODO: pass actual person image here
     26 + <img src='https://via.placeholder.com/36' className={styles.groupItemImage} alt='' />
     27 + )}
     28 + <span className={styles.groupItemName}>{item}</span>
     29 + {selectedItems.includes(item) && (
     30 + <Icon
     31 + kind='check'
     32 + width={12}
     33 + height={10}
     34 + color='#212121'
     35 + className={styles.groupItemCheck}
     36 + />
     37 + )}
     38 + </div>
     39 + );
     40 +}
     41 + 
    16 42  type Props = {
    17 43   searchValue: string;
    18 44   searchChangeHandler: (e: React.ChangeEvent<HTMLInputElement>) => void;
    19 45   clearInput: () => void;
    20  - list: GroupItem[] | [];
     46 + alphabeticalGroups: GroupItem[];
    21 47   selectedItems: string[];
    22 48   renderComponent: string;
    23 49   selectHandler: (name: string) => void;
    skipped 3 lines
    27 53   searchValue,
    28 54   searchChangeHandler,
    29 55   clearInput,
    30  - list,
     56 + alphabeticalGroups,
    31 57   selectedItems,
    32 58   renderComponent,
    33 59   selectHandler,
    skipped 28 lines
    62 88   </div>
    63 89   
    64 90   <div className={styles.groups}>
    65  - {list?.length > 0 &&
    66  - list.map(({ group, children }) => (
    67  - <div key={group}>
    68  - {list.length > 1 && <div className={styles.groupName}>{group}</div>}
    69  - <div className={styles.groupList}>
    70  - {children.map(({ name, id }: listItem) => (
    71  - <div
    72  - key={id}
    73  - className={clsx(
    74  - styles.groupItem,
    75  - selectedItems.includes(name) && styles.groupItemActive
    76  - )}
    77  - onClick={() => selectHandler(name)}
    78  - >
    79  - {renderComponent === 'person' && (
    80  - <img
    81  - src='https://via.placeholder.com/36'
    82  - className={styles.groupItemImage}
    83  - alt=''
    84  - />
    85  - )}
    86  - <span className={styles.groupItemName}>{name}</span>
    87  - {selectedItems.includes(name) && (
    88  - <Icon
    89  - kind='check'
    90  - width={12}
    91  - height={10}
    92  - color='#212121'
    93  - className={styles.groupItemCheck}
    94  - />
    95  - )}
    96  - </div>
    97  - ))}
    98  - </div>
     91 + {alphabeticalGroups.map(({ group, children }) => (
     92 + <div key={group}>
     93 + {alphabeticalGroups.length > 1 && <div className={styles.groupName}>{group}</div>}
     94 + 
     95 + <div className={styles.groupList}>
     96 + {children.map((item) => (
     97 + <SearchItem
     98 + item={item}
     99 + selectedItems={selectedItems}
     100 + selectHandler={selectHandler}
     101 + renderComponent={renderComponent}
     102 + />
     103 + ))}
    99 104   </div>
    100  - ))}
     105 + </div>
     106 + ))}
    101 107   </div>
    102 108   </>
    103 109   );
    skipped 2 lines
Please wait...
Page is in error, reload to recover