Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion client/components/AutocompleteInput/AutocompleteInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ export const AutocompleteInput = ({
<div
style={{ minWidth: inbuiltStyles.minWidth }}
className="autocomplete-input__suggestions-menu"
role="listbox"
>
{items as any}
</div>
Expand Down Expand Up @@ -112,7 +113,11 @@ export const AutocompleteInput = ({
)}
</div>
</div>
<button type="submit" className="autocomplete-input__search-icon">
<button
type="submit"
className="autocomplete-input__search-icon"
aria-label="Search package"
>
<SearchIcon className="" />
</button>
</form>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ export function SuggestionItem({ item, isHighlighted }: SuggestionItemProps) {
className={cx('autocomplete-input__suggestion', {
'autocomplete-input__suggestion--highlight': isHighlighted,
})}
role="option"
aria-selected={isHighlighted}
>
{item.highlight != null ? (
<div dangerouslySetInnerHTML={{ __html: item.highlight }} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,16 @@ export default class BuildProgressIndicator extends Component<
}

componentDidMount() {
setTimeout(() => {
this.timeoutId = setTimeout(() => {
if (!this.props.isDone) {
this.setState({ started: true })
this.setMessage()
}
}, OptimisticLoadTimeout)
}

componentWillReceiveProps(nextProps: BuildProgressIndicatorProps) {
if (nextProps.isDone) {
componentDidUpdate(prevProps: BuildProgressIndicatorProps) {
if (!prevProps.isDone && this.props.isDone) {
this.stage = 3
this.props.onDone()
}
Expand All @@ -58,7 +58,7 @@ export default class BuildProgressIndicator extends Component<
clearTimeout(this.timeoutId)
}

getProgressText = (stage: typeof order[number]) => {
getProgressText = (stage: (typeof order)[number]) => {
const progressText = {
resolving: 'Resolving version and dependencies',
building: 'Bundling package',
Expand Down
10 changes: 9 additions & 1 deletion client/components/PageNav/PageNav.scss
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

@media screen and (max-width: 40em) {
padding: $global-spacing * 2;
flex-wrap: wrap;
row-gap: $global-spacing;
}
}

Expand All @@ -17,6 +19,12 @@
display: flex;
align-items: center;
gap: 12px;

@media screen and (max-width: 40em) {
flex: 1 1 100%;
justify-content: flex-end;
margin-left: 0;
}
}

.logo-small {
Expand Down Expand Up @@ -61,7 +69,7 @@
max-width: 40vw;
overflow: scroll;
align-items: center;
justify-content: flex-end;
justify-content: flex-start;
}
}

Expand Down
2 changes: 2 additions & 0 deletions client/components/QuickStatsBar/QuickStatsBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ class QuickStatsBar extends Component<QuickStatsBarProps> {
href={'https://npmjs.com/package/' + name}
target="_blank"
rel="noopener noreferrer"
aria-label={`View ${name} on npm`}
>
<NPMIcon className="quick-stats-bar__logo-icon quick-stats-bar__logo-icon--npm" />
</a>
Expand All @@ -118,6 +119,7 @@ class QuickStatsBar extends Component<QuickStatsBarProps> {
href={repository}
target="_blank"
rel="noopener noreferrer"
aria-label={`View ${name} repository`}
>
<GithubIcon className="quick-stats-bar__logo-icon quick-stats-bar__logo-icon quick-stats-bar__logo-icon--github" />
</a>
Expand Down
3 changes: 1 addition & 2 deletions client/components/ResultLayout/ResultLayout.scss
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,7 @@
max-width: 40vw;
overflow: scroll;
align-items: center;
justify-content: flex-end;
overflow: scroll;
justify-content: flex-start;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ export default class SimilarPackageCard extends Component<SimilarPackageCardProp
e.stopPropagation()
window.location.href = pack.repository
}}
aria-label={`View ${pack.name} repository`}
>
{pack.repository.includes('github.com') ? (
<GithubIcon className="similar-package-card__github-icon" />
Expand Down
42 changes: 31 additions & 11 deletions pages/package/[...packageString]/ResultPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ import QuickStatsBar from '../../../client/components/QuickStatsBar/QuickStatsBa
import ResultLayout from '../../../client/components/ResultLayout'
import Stat from '../../../client/components/Stat'
import Warning from '../../../client/components/Warning/Warning'
import { parsePackageString } from '../../../utils/common.utils'
import {
parsePackageString,
sanitizeErrorHTML,
} from '../../../utils/common.utils'
import {
DownloadSpeed,
formatSize,
Expand Down Expand Up @@ -103,6 +106,10 @@ class ResultPage extends PureComponent<ResultPageProps, ResultPageState> {
}

private activeQuery: string | null = null
private searchRequestId = 0

private isActiveSearch = (requestId: number) =>
requestId === this.searchRequestId

componentDidMount() {
Analytics.pageView('package result')
Expand Down Expand Up @@ -135,14 +142,14 @@ class ResultPage extends PureComponent<ResultPageProps, ResultPageState> {
}
}

fetchResults = (packageString: string) => {
fetchResults = (packageString: string, requestId: number) => {
const startTime = Date.now()

API.getInfo(packageString)
.then(results => {
this.fetchSimilarPackages(packageString)
if (!this.isActiveSearch(requestId)) return

if (this.activeQuery !== packageString) return
this.fetchSimilarPackages(packageString, requestId)

const newPackageString = `${results.name}@${results.version}`
this.setState(
Expand All @@ -162,6 +169,8 @@ class ResultPage extends PureComponent<ResultPageProps, ResultPageState> {
})
})
.catch(err => {
if (!this.isActiveSearch(requestId)) return

this.setState({
resultsError: err,
resultsPromiseState: 'rejected',
Expand All @@ -175,27 +184,31 @@ class ResultPage extends PureComponent<ResultPageProps, ResultPageState> {
})
}

fetchHistory = (packageString: string) => {
fetchHistory = (packageString: string, requestId: number) => {
API.getHistory(packageString, 15)
.then(results => {
if (this.activeQuery !== packageString) return
if (!this.isActiveSearch(requestId)) return

this.setState({
historicalResultsPromiseState: 'fulfilled',
historicalResults: results,
})
})
.catch(err => {
if (!this.isActiveSearch(requestId)) return

this.setState({ historicalResultsPromiseState: 'rejected' })
console.error('Fetching history failed:', err)
})
}

fetchSimilarPackages = (packageString: string) => {
fetchSimilarPackages = (packageString: string, requestId: number) => {
const { name } = parsePackageString(packageString)

API.getSimilar(name)
.then(result => {
if (!this.isActiveSearch(requestId)) return

if (!result.category.label || result.category.score < 12) {
return
}
Expand All @@ -205,7 +218,7 @@ class ResultPage extends PureComponent<ResultPageProps, ResultPageState> {
)

Promise.allSettled(promises).then(results => {
if (this.activeQuery !== packageString) return
if (!this.isActiveSearch(requestId)) return

this.setState({
similarPackagesCategory: result.category.label ?? '',
Expand All @@ -221,6 +234,8 @@ class ResultPage extends PureComponent<ResultPageProps, ResultPageState> {
})
})
.catch(err => {
if (!this.isActiveSearch(requestId)) return

this.setState({ historicalResultsPromiseState: 'rejected' })
console.error(err)
})
Expand All @@ -229,6 +244,7 @@ class ResultPage extends PureComponent<ResultPageProps, ResultPageState> {
handleSearchSubmit = (packageString: string) => {
Analytics.performedSearch(packageString)
const normalizedQuery = packageString.trim()
const requestId = ++this.searchRequestId

this.setState(
{
Expand All @@ -242,11 +258,13 @@ class ResultPage extends PureComponent<ResultPageProps, ResultPageState> {
similarPackagesCategory: '',
},
() => {
if (!this.isActiveSearch(requestId)) return

this.activeQuery = normalizedQuery
Router.push(`/package/${normalizedQuery}`)
Analytics.pageView('package result')
this.fetchResults(normalizedQuery)
this.fetchHistory(normalizedQuery)
this.fetchResults(normalizedQuery, requestId)
this.fetchHistory(normalizedQuery, requestId)
}
)
}
Expand Down Expand Up @@ -512,7 +530,9 @@ class ResultPage extends PureComponent<ResultPageProps, ResultPageState> {
<h2 className="result-error__code">{errorName}</h2>
<p
className="result-error__message"
dangerouslySetInnerHTML={{ __html: errorBody ?? '' }}
dangerouslySetInnerHTML={{
__html: sanitizeErrorHTML(errorBody ?? ''),
}}
/>
{errorDetails && (
<details className="result-error__details">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import API, {
import SearchIcon from '../../../../../client/components/Icons/SearchIcon'
import JumpingDots from '../../../../../client/components/JumpingDots'
import { formatSize, resolveBuildError } from '../../../../../utils'
import { sanitizeErrorHTML } from '../../../../../utils/common.utils'

const State = {
TBD: 'tbd',
Expand Down Expand Up @@ -330,7 +331,11 @@ export default class ExportAnalysisSection extends Component<
return (
<div className="export-analysis-section__error">
<h4> {errorName}</h4>
<p dangerouslySetInnerHTML={{ __html: errorBody ?? '' }} />
<p
dangerouslySetInnerHTML={{
__html: sanitizeErrorHTML(errorBody ?? ''),
}}
/>
{errorDetails && <pre>{errorDetails}</pre>}
</div>
)
Expand Down
54 changes: 54 additions & 0 deletions pages/scan-results/ScanResults.scss
Original file line number Diff line number Diff line change
Expand Up @@ -221,3 +221,57 @@ $index-width: 4rem;
text-decoration: line-through;
}
}

@media screen and (max-width: 40em) {
.scan-results {
.page-content {
width: 100%;
padding: 0 $global-spacing * 2;
}
}

.scan-results__sort-panel {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: $global-spacing;

button {
margin-left: 0;
}
}

.scan-results__item {
display: grid;
grid-template-columns: 2.5rem minmax(0, 1fr);
column-gap: $global-spacing;
row-gap: $global-spacing;
}

.scan-results__index {
width: auto;
}

.scan-results__name {
width: auto;
min-width: 0;
padding-left: 0;
}

.scan-results__stat-container {
grid-column: 1 / -1;
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: $global-spacing;
width: 100%;
max-width: none;
}

.scan-results__item--total {
.scan-results__name {
grid-column: 1 / -1;
width: auto;
margin-left: 0;
}
}
}
8 changes: 6 additions & 2 deletions pages/scan-results/ScanResults.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Analytics from '../../client/analytics'
import API, { type PackageBuildInfo } from '../../client/api'
import Stat from '../../client/components/Stat'
import ResultLayout from '../../client/components/ResultLayout'
import { parsePackageString } from '../../utils/common.utils'
import { parsePackageString, sanitizeErrorHTML } from '../../utils/common.utils'
import { getTimeFromSize } from '../../utils'

type PromiseState = 'pending' | 'fulfilled' | 'rejected'
Expand Down Expand Up @@ -131,7 +131,11 @@ class ResultCard extends Component<ResultCardProps> {
content = (
<details className="scan-results__error-text">
<summary> {pack.error.code}</summary>
<p dangerouslySetInnerHTML={{ __html: pack.error.message }} />
<p
dangerouslySetInnerHTML={{
__html: sanitizeErrorHTML(pack.error.message),
}}
/>
</details>
)
break
Expand Down
18 changes: 18 additions & 0 deletions pages/scan/Scan.scss
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@
background: var(--color-btn-bg-hover);
}

&:disabled {
cursor: not-allowed;
opacity: 0.5;
}

& ~ & {
margin-left: $global-spacing * 1.5;
}
Expand Down Expand Up @@ -105,3 +110,16 @@
}
}
}

.scan__empty-selection {
@include font-size-sm;
color: var(--color-text-muted);
margin: $global-spacing * 2;
}

.scan__unsupported-packages {
@include font-size-sm;
color: var(--color-text-muted);
margin: 0 $global-spacing * 2 $global-spacing * 2;
overflow-wrap: anywhere;
}
Loading
Loading