Projects STRLCPY gradejs Commits b6a5a78e
🤬
  • ■ ■ ■ ■ ■ ■
    packages/web/src/components/layouts/Filters/Filters.module.scss
    1  -@import '~styles/responsive.scss';
    2  - 
    3  -.fields {
    4  - border: 0;
    5  -}
    6  -.fields legend {
    7  - color: #666;
    8  - padding: 0;
    9  -}
    10  - 
    11  -.dropdown {
    12  - background-color: #fff;
    13  - font-weight: normal;
    14  -}
    15  - 
    16  -.wider {
    17  - min-width: 300px;
    18  - @include mobile {
    19  - min-width: 200px;
    20  - }
    21  -}
    22  - 
    23  -.inputWider {
    24  - display: flex;
    25  -}
    26  -.inputWider > * {
    27  - flex: 1;
    28  -}
    29  - 
    30  -.form {
    31  - padding: 10px;
    32  - font-size: 13px;
    33  - line-height: 25px;
    34  - color: #0f0f0f;
    35  -}
    36  - 
    37  -.controls {
    38  - display: flex;
    39  - justify-content: space-between;
    40  -}
    41  - 
  • ■ ■ ■ ■ ■ ■
    packages/web/src/components/layouts/Filters/Filters.stories.tsx
    1  -import React from 'react';
    2  -import Filters, { Props } from './Filters';
    3  - 
    4  -export default {
    5  - title: 'Layouts / Filters dropdown',
    6  - component: Filters,
    7  - parameters: {
    8  - layout: 'centered',
    9  - },
    10  -};
    11  - 
    12  -export const Default = (args: Props) => <Filters {...args} />;
    13  -Default.args = {};
    14  - 
  • ■ ■ ■ ■ ■ ■
    packages/web/src/components/layouts/Filters/Filters.tsx
    1  -/* eslint-disable react/button-has-type */
    2  -import React, { useEffect } from 'react';
    3  -import clsx from 'clsx';
    4  -import { SubmitHandler, useForm } from 'react-hook-form';
    5  -import { Button, TextInput } from '../../ui';
    6  -import Dropdown from '../../ui/Dropdown/Dropdown';
    7  -import Radio from '../../ui/Radio/Radio';
    8  -import styles from './Filters.module.scss';
    9  -import { trackCustomEvent } from '../../../services/analytics';
    10  - 
    11  -export type Props = {
    12  - onSubmit: SubmitHandler<FiltersState>;
    13  -};
    14  - 
    15  -export type FiltersState = {
    16  - filter: 'all' | 'outdated' | 'vulnerable';
    17  - sort: 'name' | 'size' | 'severity' | 'importDepth' | 'packagePopularity' | 'confidenceScore';
    18  - filterPackageName?: string;
    19  -};
    20  - 
    21  -export const DefaultFiltersAndSorters: FiltersState = {
    22  - filter: 'all',
    23  - sort: 'severity',
    24  -};
    25  - 
    26  -export default function Filters({ onSubmit }: Props) {
    27  - const { register, handleSubmit, reset, watch } = useForm<FiltersState>({
    28  - defaultValues: DefaultFiltersAndSorters,
    29  - });
    30  - const watchFilterByName = watch('filter');
    31  - let hideHandle: () => void;
    32  - useEffect(() => {
    33  - if (typeof document !== 'undefined') {
    34  - document.body.addEventListener('click', hideHandle);
    35  - }
    36  - });
    37  - 
    38  - return (
    39  - <>
    40  - <Dropdown
    41  - getHideHandle={(handle) => {
    42  - hideHandle = handle;
    43  - }}
    44  - className={clsx(styles.wider, styles.dropdown)}
    45  - TriggerComponent={Button}
    46  - triggerChildren='Filter / Sort'
    47  - triggerArgs={{ size: 'medium' }}
    48  - position='topright'
    49  - onOpen={() => trackCustomEvent('Filters', 'OpenDropdown')}
    50  - >
    51  - <form
    52  - className={styles.form}
    53  - onSubmit={(e) => {
    54  - hideHandle();
    55  - trackCustomEvent('Filters', 'Update');
    56  - return handleSubmit(onSubmit)(e);
    57  - }}
    58  - >
    59  - <fieldset className={styles.fields}>
    60  - <legend>Filter</legend>
    61  - <Radio name='filter' value='all' register={register} appearance='justify'>
    62  - All
    63  - </Radio>
    64  - <Radio name='filter' value='outdated' register={register} appearance='justify'>
    65  - Outdated packages
    66  - </Radio>
    67  - <Radio name='filter' value='vulnerable' register={register} appearance='justify'>
    68  - Vulnerable packages
    69  - </Radio>
    70  - <Radio name='filter' value='name' register={register} appearance='justify'>
    71  - Package name
    72  - </Radio>
    73  - {watchFilterByName === 'name' && (
    74  - <div className={styles.inputWider}>
    75  - <TextInput
    76  - name='filterPackageName'
    77  - size='medium'
    78  - register={register}
    79  - placeholder='Enter package name'
    80  - />
    81  - </div>
    82  - )}
    83  - </fieldset>
    84  - <fieldset className={styles.fields}>
    85  - <legend>Sort</legend>
    86  - <Radio name='sort' value='packagePopularity' register={register} appearance='justify'>
    87  - Package popularity
    88  - </Radio>
    89  - <Radio name='sort' value='name' register={register} appearance='justify'>
    90  - Name
    91  - </Radio>
    92  - <Radio name='sort' value='size' register={register} appearance='justify'>
    93  - Size
    94  - </Radio>
    95  - <Radio name='sort' value='severity' register={register} appearance='justify'>
    96  - Vulnerability severity
    97  - </Radio>
    98  - {/* <Radio name='sort' value='importDepth' register={register} appearance='justify'>Import depth</Radio> */}
    99  - {/* <Radio name='sort' value='confidenceScore' register={register} appearance='justify'>Confidence score</Radio> */}
    100  - </fieldset>
    101  - <div className={styles.controls}>
    102  - <Button onClick={() => reset()} type='submit' size='medium'>
    103  - Reset filtering
    104  - </Button>
    105  - <Button type='submit' size='medium' variant='black'>
    106  - Apply
    107  - </Button>
    108  - </div>
    109  - </form>
    110  - </Dropdown>
    111  - </>
    112  - );
    113  -}
    114  - 
  • ■ ■ ■ ■
    packages/web/src/components/layouts/SearchResults/SearchResults.stories.tsx
    skipped 15 lines
    16 16   
    17 17  export const Loading = Template.bind({});
    18 18  Loading.args = {
    19  - pageLoading: true,
     19 + isLoading: true,
    20 20  };
    21 21   
  • ■ ■ ■ ■ ■
    packages/web/src/components/layouts/index.ts
    1 1  export { default as Home } from './Home/Home';
    2  -export { default as Website } from './Website/Website';
    3 2  export { default as SearchResults } from './SearchResults/SearchResults';
    4 3  export { default as Error } from './Error/Error';
    5 4   
  • ■ ■ ■ ■ ■ ■
    packages/web/src/components/pages/Home.tsx
    skipped 25 lines
    26 26   return (
    27 27   <Error
    28 28   host={state.address}
    29  - onReportClick={() => {
     29 + /*onReportClick={() => {
    30 30   trackCustomEvent('HomePage', 'ClickReport');
    31 31   }}
    32 32   onRetryClick={() => {
    33 33   trackCustomEvent('HomePage', 'ClickRetry');
    34 34   dispatch(resetError());
    35  - }}
     35 + }}*/
    36 36   />
    37 37   );
    38 38   }
    skipped 4 lines
  • ■ ■ ■ ■ ■ ■
    packages/web/src/components/ui/Package/Package.module.scss
    1  -@import '~styles/responsive.scss';
    2  - 
    3  -.container {
    4  - border: 1px solid #e6e6e6;
    5  - padding: 16px;
    6  - display: flex;
    7  - box-sizing: border-box;
    8  - 
    9  - &:hover .externalLinks {
    10  - opacity: 1;
    11  - }
    12  -}
    13  - 
    14  -// variants
    15  -.grid {
    16  - border-bottom-width: 1px;
    17  - height: 150px;
    18  - width: 260px;
    19  - flex-direction: column;
    20  - flex-wrap: wrap;
    21  - 
    22  - // dirty trick
    23  - // TODO: fix later
    24  - & + & + & + & + & {
    25  - margin-top: -1px;
    26  - }
    27  - 
    28  - & .registryMeta {
    29  - flex: 1;
    30  - display: flex;
    31  - justify-content: space-between;
    32  - align-items: flex-start;
    33  - }
    34  - 
    35  - & .externalLinks {
    36  - flex: 1;
    37  - display: flex;
    38  - justify-content: flex-end;
    39  - opacity: 0.5;
    40  - transition: opacity 0.3s;
    41  - }
    42  - 
    43  - & .name {
    44  - margin-bottom: 6px;
    45  - width: 100%;
    46  - }
    47  - 
    48  - & .version {
    49  - margin-right: 16px;
    50  - }
    51  - 
    52  - & .meta {
    53  - display: flex;
    54  - flex-wrap: wrap;
    55  - }
    56  -}
    57  - 
    58  -.lines {
    59  - min-height: 66px;
    60  - width: 100%;
    61  - flex-direction: row;
    62  - align-items: center;
    63  - border-left-width: 0;
    64  - border-right-width: 0;
    65  - padding-left: 0;
    66  - justify-content: space-between;
    67  - 
    68  - // dirty trick
    69  - // TODO: fix later
    70  - & + & {
    71  - margin-top: -1px;
    72  - }
    73  - 
    74  - & .name {
    75  - margin-right: 8px;
    76  - flex: 1;
    77  - }
    78  - 
    79  - & .registryMeta {
    80  - justify-self: flex-end;
    81  - order: 10;
    82  - flex: 1;
    83  - display: flex;
    84  - justify-content: flex-end;
    85  - align-items: center;
    86  - 
    87  - @include mobile {
    88  - display: none;
    89  - }
    90  - }
    91  - 
    92  - & .meta {
    93  - flex: 1;
    94  - display: flex;
    95  - justify-content: space-between;
    96  - }
    97  -}
    98  - 
    99  -.name {
    100  - appearance: none;
    101  - text-decoration: none;
    102  - font-size: 18px;
    103  - line-height: 140%;
    104  - color: #0f0f0f;
    105  - 
    106  - &:hover {
    107  - text-decoration: underline;
    108  - }
    109  -}
    110  - 
    111  -.version {
    112  - font-size: 14px;
    113  - line-height: 140%;
    114  - color: #666666;
    115  -}
    116  - 
    117  -.size {
    118  - font-size: 14px;
    119  - line-height: 140%;
    120  - color: #666666;
    121  -}
    122  - 
    123  -.packageTags {
    124  - display: flex;
    125  - flex-direction: column;
    126  -}
    127  - 
    128  -.tagContainer {
    129  - margin-bottom: 5px;
    130  -}
    131  - 
    132  -.vulnerabilityTooltip {
    133  - padding: 10px;
    134  - background: #fff;
    135  -}
    136  - 
    137  -.externalLink {
    138  - margin-left: 6px;
    139  -}
    140  - 
    141  -.externalLinks {
    142  - opacity: 0.5;
    143  - transition: opacity 0.3s;
    144  -}
    145  - 
  • ■ ■ ■ ■ ■ ■
    packages/web/src/components/ui/Package/Package.stories.tsx
    1  -import React from 'react';
    2  -import Package, { Props } from './Package';
    3  - 
    4  -export default {
    5  - title: 'Interface / Package',
    6  - component: Package,
    7  - parameters: {
    8  - layout: 'centered',
    9  - },
    10  -};
    11  - 
    12  -export const Default = (args: Props) => <Package {...args} />;
    13  -Default.args = {
    14  - variant: 'grid',
    15  - pkg: {
    16  - packageName: 'react',
    17  - packageVersionRange: '17.0.0 - 17.0.2',
    18  - packageMetadata: {
    19  - approximateByteSize: 2352,
    20  - },
    21  - },
    22  -};
    23  - 
  • ■ ■ ■ ■ ■ ■
    packages/web/src/components/ui/Package/Package.tsx
    1  -/* eslint-disable react/button-has-type */
    2  -import clsx from 'clsx';
    3  -import React from 'react';
    4  -import semver from 'semver';
    5  -import styles from './Package.module.scss';
    6  -import Dropdown from '../Dropdown/Dropdown';
    7  -import Vulnerability from '../Vulnerability/Vulnerability';
    8  -import TagBadge from '../TagBadge/TagBadge';
    9  -import { trackCustomEvent } from '../../../services/analytics';
    10  -import { ClientApi } from '../../../services/apiClient';
    11  -import { Icon } from '../Icon/Icon';
    12  - 
    13  -export type Props = {
    14  - className?: string;
    15  - variant?: 'grid' | 'lines';
    16  - pkg: ClientApi.ScanResultPackageResponse;
    17  - vulnerabilities: ClientApi.PackageVulnerabilityResponse[];
    18  -};
    19  - 
    20  -export default function Package({ className, variant = 'grid', pkg, vulnerabilities }: Props) {
    21  - const repositoryUrl = pkg.registryMetadata?.repositoryUrl;
    22  - const homepageUrl = pkg.registryMetadata?.homepageUrl;
    23  - const isOutdated =
    24  - pkg.registryMetadata && semver.gtr(pkg.registryMetadata.latestVersion, pkg.versionRange);
    25  - const isVulnerable = !!vulnerabilities?.length;
    26  - 
    27  - return (
    28  - <div className={clsx(styles.container, styles[variant], className)}>
    29  - <div className={styles.registryMeta}>
    30  - <div className={styles.packageTags}>
    31  - {isVulnerable && (
    32  - <Dropdown
    33  - TriggerComponent={(props) => (
    34  - <span className={styles.tagContainer}>
    35  - <TagBadge color='red' {...props}>
    36  - Vulnerable
    37  - </TagBadge>
    38  - </span>
    39  - )}
    40  - triggerType='hover'
    41  - position='bottomleft'
    42  - onOpen={() => trackCustomEvent('Package', 'ShowVulnerabilitiesTooltip')}
    43  - >
    44  - <div className={styles.vulnerabilityTooltip}>
    45  - {vulnerabilities.map((it) => (
    46  - <Vulnerability key={it.osvId} vulnerability={it} />
    47  - ))}
    48  - </div>
    49  - </Dropdown>
    50  - )}
    51  - {isOutdated && (
    52  - <span className={styles.tagContainer}>
    53  - <TagBadge color='yellow'>Outdated</TagBadge>
    54  - </span>
    55  - )}
    56  - </div>
    57  - <div className={styles.externalLinks}>
    58  - {repositoryUrl && (
    59  - <a
    60  - href={repositoryUrl}
    61  - target='_blank'
    62  - rel='noopener noreferrer'
    63  - onClick={() => trackCustomEvent('Package', 'ClickRepoUrl')}
    64  - className={styles.externalLink}
    65  - >
    66  - <Icon kind='githubLogo' width={19} height={19} />
    67  - </a>
    68  - )}
    69  - {homepageUrl && homepageUrl !== repositoryUrl && (
    70  - <a
    71  - href={homepageUrl}
    72  - target='_blank'
    73  - rel='noopener noreferrer'
    74  - onClick={() => trackCustomEvent('Package', 'ClickHomepageUrl')}
    75  - className={styles.externalLink}
    76  - >
    77  - <Icon kind='external' width={19} height={19} />
    78  - </a>
    79  - )}
    80  - </div>
    81  - </div>
    82  - <a
    83  - className={styles.name}
    84  - href={`https://www.npmjs.com/package/${pkg.name}`}
    85  - target='_blank'
    86  - rel='noopener noreferrer'
    87  - aria-label={pkg.name}
    88  - // Browser won't break a line for '/' symbol, so we add the <wbr> specificaly
    89  - // eslint-disable-next-line react/no-danger
    90  - dangerouslySetInnerHTML={{ __html: pkg.name.replace('/', '/<wbr>') }}
    91  - onClick={() => trackCustomEvent('Package', 'ClickPackageUrl')}
    92  - />
    93  - <div className={styles.meta}>
    94  - <div className={styles.version}>{toReadableVersion(pkg.versionRange)}</div>
    95  - {!!pkg.approximateByteSize && (
    96  - <span className={styles.size}>{toReadableSize(pkg.approximateByteSize)}</span>
    97  - )}
    98  - </div>
    99  - </div>
    100  - );
    101  -}
    102  - 
    103  -const byteUnits = ['B', 'KB', 'MB'];
    104  -function toReadableSize(size: number) {
    105  - let byteUnitIndex = 0;
    106  - let sizeInUnits = size;
    107  - 
    108  - while (sizeInUnits > 1024) {
    109  - sizeInUnits /= 1024;
    110  - byteUnitIndex += 1;
    111  - }
    112  - 
    113  - // parseFloat(X.toFixed(1)) removes a zero fraction
    114  - return `${parseFloat(sizeInUnits.toFixed(1))}${byteUnits[byteUnitIndex]}`;
    115  -}
    116  - 
    117  -function toReadableVersion(version: string) {
    118  - if (version === '*') {
    119  - return 'Unknown version';
    120  - }
    121  - 
    122  - return version;
    123  -}
    124  - 
  • ■ ■ ■ ■ ■ ■
    packages/web/src/components/ui/PackagePreview/PackagePreview.stories.tsx
    skipped 1 lines
    2 2  import { ComponentStory, ComponentMeta } from '@storybook/react';
    3 3  import PackagePreview from './PackagePreview';
    4 4  import { PackagePreviewSkeleton } from './PackagePreviewSkeleton';
     5 +import { IdentifiedPackage } from 'store/selectors/websiteResults';
    5 6   
    6 7  export default {
    7 8   title: 'Interface / PackagePreview',
    skipped 3 lines
    11 12   },
    12 13  } as ComponentMeta<typeof PackagePreview>;
    13 14   
     15 +const pkg: IdentifiedPackage = {
     16 + name: '@team-griffin/react-heading-section',
     17 + version: '3.0.0 - 4.16.4',
     18 + versionSet: ['3.0.0', '3.0.1', '4.16.4'],
     19 + outdated: false,
     20 + vulnerable: false,
     21 + duplicate: false,
     22 + approximateByteSize: 123456,
     23 + moduleIds: [],
     24 + registryMetadata: {
     25 + name: '@team-griffin/react-heading-section',
     26 + latestVersion: '4.16.4',
     27 + monthlyDownloads: 1234,
     28 + description: 'The Lodash library exported as ES modules. Generated using lodash-cli',
     29 + fullDescription:
     30 + 'Full description: the Lodash library exported as ES modules. Generated using lodash-cli',
     31 + maintainers: [
     32 + { name: 'jdalton', email: '[email protected]', avatar: 'https://via.placeholder.com/36' },
     33 + ],
     34 + keywords: ['#moment', '#date', '#time', '#parse', '#format', '#format', '#format'],
     35 + versionSpecificValues: {},
     36 + homepageUrl: 'https://gradejs.com',
     37 + repositoryUrl: 'https://github.com/gradejs/gradejs',
     38 + license: 'MIT',
     39 + updateSeq: 0,
     40 + updatedAt: new Date(),
     41 + } as any,
     42 +};
     43 + 
    14 44  export const ClosedLoading: ComponentStory<typeof PackagePreview> = () => (
    15 45   <PackagePreviewSkeleton />
    16 46  );
    17  -export const Closed: ComponentStory<typeof PackagePreview> = () => (
    18  - <PackagePreview
    19  - name='@team-griffin/react-heading-section'
    20  - version='3.0.0 - 4.16.4'
    21  - desc='The Lodash library exported as ES modules. Generated using lodash-cli'
    22  - problems={['vulnerabilities']}
    23  - keywords={['#moment', '#date', '#time', '#parse', '#format', '#format', '#format']}
    24  - author={{ name: 'jdalton', image: 'https://via.placeholder.com/36' }}
    25  - />
    26  -);
     47 +export const Closed: ComponentStory<typeof PackagePreview> = () => <PackagePreview pkg={pkg} />;
    27 48   
    28 49  export const OpenedLoading: ComponentStory<typeof PackagePreview> = () => (
    29  - <PackagePreview
    30  - name='@team-griffin/react-heading-section'
    31  - version='3.0.0 - 4.16.4'
    32  - desc='The Lodash library exported as ES modules. Generated using lodash-cli'
    33  - problems={['vulnerabilities']}
    34  - keywords={['#moment', '#date', '#time', '#parse', '#format', '#format', '#format']}
    35  - author={{ name: 'jdalton', image: 'https://via.placeholder.com/36' }}
    36  - opened
    37  - detailsLoading
    38  - />
     50 + <PackagePreview pkg={pkg} opened detailsLoading />
    39 51  );
    40 52   
    41 53  export const Opened: ComponentStory<typeof PackagePreview> = () => (
    42  - <PackagePreview
    43  - name='@team-griffin/react-heading-section'
    44  - version='3.0.0 - 4.16.4'
    45  - desc='The Lodash library exported as ES modules. Generated using lodash-cli'
    46  - problems={['vulnerabilities']}
    47  - keywords={['#moment', '#date', '#time', '#parse', '#format', '#format', '#format']}
    48  - author={{ name: 'jdalton', image: 'https://via.placeholder.com/36' }}
    49  - opened
    50  - />
     54 + <PackagePreview pkg={pkg} opened />
    51 55  );
    52 56   
  • ■ ■ ■ ■
    packages/web/src/components/ui/PackagePreview/PackagePreview.tsx
    skipped 5 lines
    6 6  import ChipGroup from '../ChipGroup/ChipGroup';
    7 7  import { CSSTransition } from 'react-transition-group';
    8 8  import Button from '../Button/Button';
    9  -import { LicenceSkeleton, LinksSkeleton, ScriptSkeleton } from './PackagePreviewSkeleton';
     9 +import { LicenceSkeleton, LinksSkeleton } from './PackagePreviewSkeleton';
    10 10  import ProblemBadge from '../ProblemBadge/ProblemBadge';
    11 11  import { ChipGroupSkeleton } from '../ChipGroup/ChipGroupSkeleton';
    12 12  import { useNavigate } from 'react-router-dom';
    skipped 297 lines
  • ■ ■ ■ ■ ■
    packages/web/src/components/ui/index.ts
    skipped 3 lines
    4 4  export { default as Logo } from './Logo/Logo';
    5 5  export { default as Section } from './Section/Section';
    6 6  export { default as TextInput } from './TextInput/TextInput';
    7  -export { default as Package } from './Package/Package';
    8 7  export { default as PackageSkeleton } from './PackageSkeleton/PackageSkeleton';
    9 8  export { default as Wrapper } from './Wrapper/Wrapper';
    10 9   
  • ■ ■ ■ ■
    packages/web/src/store/slices/home.ts
    skipped 6 lines
    7 7   url = `https://${url}`;
    8 8   }
    9 9   
    10  - await client.mutation('requestWebPageScan', url);
     10 + await client.mutation('requestWebPageRescan', url);
    11 11  });
    12 12   
    13 13  const home = createSlice({
    skipped 37 lines
Please wait...
Page is in error, reload to recover