Skip to content

Commit 0990a75

Browse files
committed
Expose UriProvider and refactor context usage
1 parent 4c52214 commit 0990a75

File tree

4 files changed

+47
-30
lines changed

4 files changed

+47
-30
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
99
### Added
1010
- Add `<CustomRendering/>` to override presentation ([#382](https://github.com/cucumber/react-components/pull/382))
1111
- Add `<ControlledSearchProvider/>`, `<InMemorySearchProvider/>` and `<UrlSearchProvider/>` to provide search state ([#384](https://github.com/cucumber/react-components/pull/384))
12+
- Add `<UriProvider/>`, to provide document URI state ([#386](https://github.com/cucumber/react-components/pull/386))
1213

1314
### Fixed
1415
- Make keyword spacing look right ([#376](https://github.com/cucumber/react-components/pull/376))
1516
- Fix issue with hook steps not being rendered ([#379](https://github.com/cucumber/react-components/pull/379))
1617
- Rounding down of percent passed and adding one decimal place ([#380](https://github.com/cucumber/react-components/pull/380))
18+
- Avoid UUID errors from accordion when document URI contains spaces ([#386](https://github.com/cucumber/react-components/pull/386))
1719

1820
### Changed
1921
- Inherit font-size instead of setting at 14px ([#377](https://github.com/cucumber/react-components/pull/377))

src/components/app/GherkinDocumentList.tsx

Lines changed: 37 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as messages from '@cucumber/messages'
22
import { getWorstTestStepResult } from '@cucumber/messages'
33
import { faChevronRight } from '@fortawesome/free-solid-svg-icons'
44
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
5-
import React, { FunctionComponent, useContext, useMemo, useState } from 'react'
5+
import React, { FC, useMemo, useState } from 'react'
66
import {
77
Accordion,
88
AccordionItem,
@@ -11,26 +11,37 @@ import {
1111
AccordionItemPanel,
1212
} from 'react-accessible-accordion'
1313

14-
import CucumberQueryContext from '../../CucumberQueryContext.js'
15-
import GherkinQueryContext from '../../GherkinQueryContext.js'
16-
import UriContext from '../../UriContext.js'
14+
import { useQueries } from '../../hooks/index.js'
1715
import { GherkinDocument, MDG, StatusIcon } from '../gherkin/index.js'
1816
import styles from './GherkinDocumentList.module.scss'
17+
import { UriProvider } from './UriProvider.js'
1918

20-
interface IProps {
21-
gherkinDocuments?: readonly messages.GherkinDocument[] | null
19+
interface ValidGherkinDocument extends messages.GherkinDocument {
20+
uri: string
21+
}
22+
23+
interface Props {
24+
gherkinDocuments?: readonly messages.GherkinDocument[]
2225
// Set to true if non-PASSED documents should be pre-expanded
2326
preExpand?: boolean
2427
}
2528

26-
export const GherkinDocumentList: FunctionComponent<IProps> = ({ gherkinDocuments, preExpand }) => {
27-
const gherkinQuery = useContext(GherkinQueryContext)
28-
const cucumberQuery = useContext(CucumberQueryContext)
29-
const gherkinDocs = gherkinDocuments || gherkinQuery.getGherkinDocuments()
30-
const gherkinDocumentStatusByUri = useMemo(() => {
31-
const entries: Array<[string, messages.TestStepResultStatus]> = gherkinDocs.map(
29+
const idByUri = new Map<string, string>()
30+
function getIdByUri(uri: string): string {
31+
if (!idByUri.has(uri)) {
32+
idByUri.set(uri, crypto.randomUUID())
33+
}
34+
return idByUri.get(uri) as string
35+
}
36+
37+
export const GherkinDocumentList: FC<Props> = ({ gherkinDocuments, preExpand }) => {
38+
const { gherkinQuery, cucumberQuery } = useQueries()
39+
const documents = (gherkinDocuments || gherkinQuery.getGherkinDocuments()).filter(
40+
(doc) => !!doc.uri
41+
) as ReadonlyArray<ValidGherkinDocument>
42+
const statusByUri = useMemo(() => {
43+
const entries: Array<[string, messages.TestStepResultStatus]> = documents.map(
3244
(gherkinDocument) => {
33-
if (!gherkinDocument.uri) throw new Error('No url for gherkinDocument')
3445
const gherkinDocumentStatus = gherkinDocument.feature
3546
? getWorstTestStepResult(
3647
cucumberQuery.getPickleTestStepResults(gherkinQuery.getPickleIds(gherkinDocument.uri))
@@ -40,17 +51,13 @@ export const GherkinDocumentList: FunctionComponent<IProps> = ({ gherkinDocument
4051
}
4152
)
4253
return new Map(entries)
43-
}, [gherkinDocs, gherkinQuery, cucumberQuery])
54+
}, [documents, gherkinQuery, cucumberQuery])
4455
const [expanded, setExpanded] = useState<Array<string | number>>(() => {
4556
// Pre-expand any document that is *not* passed - assuming this is what people want to look at first
4657
return preExpand
47-
? (gherkinDocs
48-
.filter(
49-
(doc) =>
50-
doc.uri &&
51-
gherkinDocumentStatusByUri.get(doc.uri) !== messages.TestStepResultStatus.PASSED
52-
)
53-
.map((doc) => doc.uri) as string[])
58+
? (documents
59+
.filter((doc) => statusByUri.get(doc.uri) !== messages.TestStepResultStatus.PASSED)
60+
.map((doc) => getIdByUri(doc.uri)) as string[])
5461
: []
5562
})
5663

@@ -62,15 +69,15 @@ export const GherkinDocumentList: FunctionComponent<IProps> = ({ gherkinDocument
6269
onChange={setExpanded}
6370
className={styles.accordion}
6471
>
65-
{gherkinDocs.map((doc) => {
66-
if (!doc.uri) throw new Error('No url for gherkinDocument')
67-
const gherkinDocumentStatus = gherkinDocumentStatusByUri.get(doc.uri)
68-
if (!gherkinDocumentStatus) throw new Error(`No status for ${doc.uri}`)
72+
{documents.map((doc) => {
73+
const id = getIdByUri(doc.uri)
74+
const status = statusByUri.get(doc.uri)
75+
if (!status) throw new Error(`No status for ${doc.uri}`)
6976
const source = gherkinQuery.getSource(doc.uri)
7077
if (!source) throw new Error(`No source for ${doc.uri}`)
7178

7279
return (
73-
<AccordionItem key={doc.uri} uuid={doc.uri} className={styles.accordionItem}>
80+
<AccordionItem key={id} uuid={id} className={styles.accordionItem}>
7481
<AccordionItemHeading>
7582
<AccordionItemButton className={styles.accordionButton}>
7683
<FontAwesomeIcon
@@ -79,20 +86,20 @@ export const GherkinDocumentList: FunctionComponent<IProps> = ({ gherkinDocument
7986
icon={faChevronRight}
8087
/>
8188
<span className={styles.icon}>
82-
<StatusIcon status={gherkinDocumentStatus} />
89+
<StatusIcon status={status} />
8390
</span>
8491
<span>{doc.uri}</span>
8592
</AccordionItemButton>
8693
</AccordionItemHeading>
87-
{expanded.includes(doc.uri) && (
94+
{expanded.includes(id) && (
8895
<AccordionItemPanel className={styles.accordionPanel}>
89-
<UriContext.Provider value={doc.uri}>
96+
<UriProvider uri={doc.uri}>
9097
{source.mediaType === messages.SourceMediaType.TEXT_X_CUCUMBER_GHERKIN_PLAIN ? (
9198
<GherkinDocument gherkinDocument={doc} source={source} />
9299
) : (
93100
<MDG uri={doc.uri}>{source.data}</MDG>
94101
)}
95-
</UriContext.Provider>
102+
</UriProvider>
96103
</AccordionItemPanel>
97104
)}
98105
</AccordionItem>

src/components/app/UriProvider.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import React, { FC, PropsWithChildren } from 'react'
2+
3+
import UriContext from '../../UriContext.js'
4+
5+
export const UriProvider: FC<PropsWithChildren<{ uri: string }>> = ({ uri, children }) => {
6+
return <UriContext.Provider value={uri}>{children}</UriContext.Provider>
7+
}

src/components/app/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ export * from './NoMatchResult.js'
1010
export * from './QueriesProvider.js'
1111
export * from './SearchBar.js'
1212
export * from './StatusesSummary.js'
13+
export * from './UriProvider.js'
1314
export * from './UrlSearchProvider.js'

0 commit comments

Comments
 (0)