Skip to content

Commit 43f34d7

Browse files
authored
allow showing detail of every example, simplify examples table (#159)
* first pass at showing a drilldown * remove unused export * rework list components * add some testing * refactor a bit * get passing for status and step text * remove rendering of errors and attachment within examples table * add tags, refactor testing * add more testing of non-success cases * add dynamic wording on back button * styling for back button * click row to drill down * remove unused import * remove unused imports * make datatable and docstrint components accept pickle versions * render datatable and docstring from pickle if present * update CHANGELOG.md
1 parent 7aac867 commit 43f34d7

26 files changed

+392
-203
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
66
and this project adheres to [Semantic Versioning](http://semver.org/).
77

88
## [Unreleased]
9+
### Changed
10+
- Allow showing detail of every example, simplify examples table ([#159](https://github.com/cucumber/react-components/pull/159))
911

1012
## [20.0.2] - 2022-04-14
1113
### Fixed

src/components/customise/customRendering.tsx

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as messages from '@cucumber/messages'
2+
import { Pickle, PickleStep } from '@cucumber/messages'
23
import React, { useContext } from 'react'
34

45
function mixinStyles<Classes>(
@@ -35,14 +36,12 @@ export interface BackgroundProps {
3536
background: messages.Background
3637
}
3738

38-
export type BackgroundClasses = Styles<'steps'>
39-
4039
export type ChildrenProps = Record<string, unknown>
4140

4241
export type ChildrenClasses = Styles<'children'>
4342

4443
export interface DataTableProps {
45-
dataTable: messages.DataTable
44+
dataTable: messages.DataTable | messages.PickleTable
4645
}
4746

4847
export type DataTableClasses = Styles<'table'>
@@ -54,7 +53,7 @@ export interface DescriptionProps {
5453
export type DescriptionClasses = Styles<'content'>
5554

5655
export interface DocStringProps {
57-
docString: messages.DocString
56+
docString: messages.DocString | messages.PickleDocString
5857
}
5958

6059
export type DocStringClasses = Styles<'docString'>
@@ -74,7 +73,7 @@ export interface ExamplesTableProps {
7473
tableBody: readonly messages.TableRow[]
7574
}
7675

77-
export type ExamplesTableClasses = Styles<'examplesTable' | 'detailRow'>
76+
export type ExamplesTableClasses = Styles<'examplesTable'>
7877

7978
export interface FeatureProps {
8079
feature: messages.Feature
@@ -87,6 +86,7 @@ export interface GherkinDocumentProps {
8786

8887
export interface GherkinStepProps {
8988
step: messages.Step
89+
pickleStep?: PickleStep
9090
hasExamples: boolean
9191
}
9292

@@ -100,6 +100,7 @@ export type KeywordClasses = Styles<'keyword'>
100100

101101
export interface ParameterProps {
102102
parameterTypeName: string
103+
value: string
103104
}
104105

105106
export type ParameterClasses = Styles<'parameter'>
@@ -116,17 +117,16 @@ export interface ScenarioProps {
116117
scenario: messages.Scenario
117118
}
118119

119-
export type ScenarioClasses = Styles<'steps'>
120-
121120
export type StatusIconClasses = Styles<'icon'>
122121

123-
export interface StepListProps {
122+
export interface GherkinStepsProps {
124123
steps: readonly messages.Step[]
124+
pickle?: Pickle
125125
hasExamples: boolean
126126
}
127127

128128
export interface TagsProps {
129-
tags: readonly messages.Tag[]
129+
tags: readonly messages.Tag[] | readonly messages.PickleTag[]
130130
}
131131

132132
export type TagsClasses = Styles<'tags' | 'tag'>
@@ -159,7 +159,7 @@ export declare type Customised<Props, Classes = Record<string, string>> =
159159

160160
export interface CustomRenderingSupport {
161161
Anchor?: Customised<AnchorProps, AnchorClasses>
162-
Background?: Customised<BackgroundProps, BackgroundClasses>
162+
Background?: Customised<BackgroundProps>
163163
Attachment?: Customised<AttachmentProps, AttachmentClasses>
164164
Children?: Customised<ChildrenProps, ChildrenClasses>
165165
DataTable?: Customised<DataTableProps, DataTableClasses>
@@ -171,13 +171,13 @@ export interface CustomRenderingSupport {
171171
Feature?: Customised<FeatureProps>
172172
GherkinDocument?: Customised<GherkinDocumentProps>
173173
GherkinStep?: Customised<GherkinStepProps>
174+
GherkinSteps?: Customised<GherkinStepsProps>
174175
HookStep?: Customised<HookStepProps>
175176
Keyword?: Customised<unknown, KeywordClasses>
176177
Parameter?: Customised<ParameterProps, ParameterClasses>
177178
Rule?: Customised<RuleProps>
178-
Scenario?: Customised<ScenarioProps, ScenarioClasses>
179+
Scenario?: Customised<ScenarioProps>
179180
StatusIcon?: Customised<StatusIconProps, StatusIconClasses>
180-
StepList?: Customised<StepListProps>
181181
Tags?: Customised<TagsProps, TagsClasses>
182182
Title?: Customised<TitleProps, TitleClasses>
183183
}

src/components/gherkin/Background.tsx

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,28 @@
11
import React from 'react'
22

3-
import {
4-
BackgroundClasses,
5-
BackgroundProps,
6-
DefaultComponent,
7-
useCustomRendering,
8-
} from '../customise'
9-
import defaultStyles from './Background.module.scss'
3+
import { BackgroundProps, DefaultComponent, useCustomRendering } from '../customise'
104
import { Description } from './Description'
5+
import { GherkinSteps } from './GherkinSteps'
116
import { Keyword } from './Keyword'
12-
import { StepList } from './StepList'
7+
import { StepsList } from './StepsList'
138
import { Title } from './Title'
149

15-
const DefaultRenderer: DefaultComponent<BackgroundProps, BackgroundClasses> = ({
16-
background,
17-
styles,
18-
}) => {
10+
const DefaultRenderer: DefaultComponent<BackgroundProps> = ({ background }) => {
1911
return (
2012
<section>
2113
<Title header="h2" id={background.id}>
2214
<Keyword>{background.keyword}:</Keyword>
2315
<span>{background.name}</span>
2416
</Title>
2517
<Description description={background.description} />
26-
<ol className={styles.steps}>
27-
<StepList steps={background.steps || []} hasExamples={false} />
28-
</ol>
18+
<StepsList>
19+
<GherkinSteps steps={background.steps || []} hasExamples={false} />
20+
</StepsList>
2921
</section>
3022
)
3123
}
3224

3325
export const Background: React.FunctionComponent<BackgroundProps> = (props) => {
34-
const ResolvedRenderer = useCustomRendering<BackgroundProps, BackgroundClasses>(
35-
'Background',
36-
defaultStyles,
37-
DefaultRenderer
38-
)
26+
const ResolvedRenderer = useCustomRendering<BackgroundProps>('Background', {}, DefaultRenderer)
3927
return <ResolvedRenderer {...props} />
4028
}
Lines changed: 1 addition & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,5 @@
1-
@import '../../styles/theming';
2-
3-
@mixin cucumber-table-style {
4-
border-collapse: collapse;
5-
margin: 0;
6-
7-
thead tr {
8-
border-bottom: 6px solid transparent;
9-
}
10-
11-
tr + tr {
12-
border-top: 6px solid transparent;
13-
}
14-
15-
th, td {
16-
padding: 0.4em 0.6em;
17-
border-width: 2px;
18-
border-style: solid;
19-
border-top: none;
20-
border-bottom: none;
21-
border-color: $keywordColor;
22-
color: $parameterColor;
23-
}
24-
}
1+
@import '../../styles/tables';
252

263
.table {
274
@include cucumber-table-style;
285
}
29-
30-
.examplesTable {
31-
@include cucumber-table-style;
32-
33-
th:first-child, td:first-child {
34-
border-left: none;
35-
}
36-
}
37-
38-
.detailRow.detailRow {
39-
border-top: none;
40-
}
41-

src/components/gherkin/DataTable.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import defaultStyles from './DataTable.module.scss'
1212
import isNumber from './isNumber'
1313

1414
const TableBody: React.FunctionComponent<{
15-
rows: readonly messages.TableRow[]
15+
rows: readonly messages.TableRow[] | readonly messages.PickleTableRow[]
1616
}> = ({ rows }) => {
1717
return (
1818
<tbody>
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
@import '../../styles/theming';
2+
3+
.backButton {
4+
display: flex;
5+
align-items: center;
6+
gap: 4px;
7+
background-color: transparent;
8+
color: $anchorColor;
9+
font-family: inherit;
10+
font-size: inherit;
11+
padding: 0;
12+
border: 0;
13+
margin: 0 0 0.5em 0;
14+
cursor: pointer;
15+
16+
&:hover,
17+
&:focus {
18+
text-decoration: underline;
19+
}
20+
21+
svg {
22+
transform: rotate(45deg);
23+
}
24+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { Pickle, Scenario } from '@cucumber/messages'
2+
import { faArrowLeft } from '@fortawesome/free-solid-svg-icons'
3+
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
4+
import React, { useContext, VoidFunctionComponent } from 'react'
5+
6+
import CucumberQueryContext from '../../CucumberQueryContext'
7+
import GherkinQueryContext from '../../GherkinQueryContext'
8+
import { HighLight } from '../app/HighLight'
9+
import { Description } from './Description'
10+
import styles from './ExampleDetail.module.scss'
11+
import { GherkinSteps } from './GherkinSteps'
12+
import { HookSteps } from './HookSteps'
13+
import { Keyword } from './Keyword'
14+
import { StepsList } from './StepsList'
15+
import { Tags } from './Tags'
16+
import { Title } from './Title'
17+
18+
export const ExampleDetail: VoidFunctionComponent<{
19+
scenario: Scenario
20+
pickleId: string
21+
onBack: () => void
22+
}> = ({ scenario, pickleId, onBack }) => {
23+
const gherkinQuery = useContext(GherkinQueryContext)
24+
const cucumberQuery = useContext(CucumberQueryContext)
25+
const pickle: Pickle = gherkinQuery.getPickles().find((item) => item.id === pickleId) as Pickle
26+
const beforeHooks = cucumberQuery.getBeforeHookSteps(pickleId)
27+
const afterHooks = cucumberQuery.getAfterHookSteps(pickleId)
28+
const examplesCount = scenario.examples.flatMap((examples) => examples.tableBody).length
29+
return (
30+
<>
31+
<button className={styles.backButton} onClick={onBack}>
32+
<FontAwesomeIcon icon={faArrowLeft} />
33+
Back to outline and all {examplesCount} examples
34+
</button>
35+
<section>
36+
<Tags tags={pickle.tags} />
37+
<Title header="h2" id={scenario.id}>
38+
<Keyword>Example:</Keyword>
39+
<HighLight text={pickle.name} />
40+
</Title>
41+
<Description description={scenario.description} />
42+
<StepsList>
43+
<HookSteps hookSteps={beforeHooks} />
44+
<GherkinSteps steps={scenario.steps || []} pickle={pickle} hasExamples={false} />
45+
<HookSteps hookSteps={afterHooks} />
46+
</StepsList>
47+
</section>
48+
</>
49+
)
50+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { createContext } from 'react'
2+
3+
export const ExamplesContext = createContext<{
4+
setSelectedExample: (pickleId?: string) => void
5+
}>({
6+
setSelectedExample: () => undefined,
7+
})
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
@import '../../styles/theming';
2+
@import '../../styles/tables';
3+
4+
.examplesTable {
5+
@include cucumber-table-style;
6+
7+
th:first-child, td:first-child {
8+
border-left: none;
9+
}
10+
11+
tbody tr {
12+
cursor: pointer;
13+
}
14+
}

0 commit comments

Comments
 (0)