Skip to content

Commit 2fc3fda

Browse files
Header Tool-Bar (#65)
* Style and Doc fixes * Add header tool-bar * Adjust unit test * Define default alignment for header items * Bump minor version
1 parent ba55828 commit 2fc3fda

File tree

8 files changed

+109
-9
lines changed

8 files changed

+109
-9
lines changed

CHANGELOG.MD

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

77
## [Unreleased]
8+
### Added
9+
- allow for addition of tool-bar items in the header besides the search input via `renderHeaderToolBar` prop
10+
11+
### Fixed
12+
- height of breadcrumb items should not grow with custom trailing content
813

914
## [4.0.1] - 2019-11-01
1015
### Fixed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ npm i react-jsonschema-inspector
5757
| `searchOptions.inputPlaceholder` | String: for setting the input hint in the search field. This defaults to `"Search"`. |
5858
| `searchOptions.debounceWait` | Number indicating the delay in milliseconds between the last change to the search term being entered and it actually being applied. This defaults to `200` but may be increased when used with exceptionally large schemas and you experience performance issues. Please refer to the documentation on [`lodash.debounce`](https://lodash.com/docs/4.17.11#debounce). |
5959
| `searchOptions.debounceMaxWait` | Number indicating the maximum delay in milliseconds after the search term was changed. This defaults to `500`. Please refer to the documentation on [`lodash.debounce`](https://lodash.com/docs/4.17.11#debounce). |
60+
| `renderHeaderToolBar` | Function: custom render function for additional header tool-bar besides search input. Receives one parameter: object with a: "columnData" property |
6061
| `renderItemContent` | Function: custom render function for name of single property/sub-schema in a column. Receives one parameter: object with the following properties: "name", "hasNestedItems", "selected", "schemaGroup" |
6162
| `renderSelectionDetails` | Function: custom render function for the "Details" block on the right for the single property/sub-schema being selected. Receives one parameter: object with the following properties: "itemSchemaGroup", "columnData", "selectionColumnIndex", "optionIndexes" |
6263
| `renderEmptyDetails` | Function: custom render function for the "Details" block on the right if nothing is selected yet. Receives one parameter, which is an object with the "rootColumnSchemas" property, which holds the array of top-level schemas (as derived from the `schemas` prop and augmented by any given `referenceSchemas`)

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-jsonschema-inspector",
3-
"version": "4.0.2-SNAPSHOT",
3+
"version": "4.1.0-SNAPSHOT",
44
"description": "View component for traversing/searching in a JSON Schema",
55
"homepage": "https://CarstenWickner.github.io/react-jsonschema-inspector",
66
"author": "Carsten Wickner",

src/component/Inspector.scss

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,13 +154,23 @@
154154
&-header {
155155
flex-direction: row-reverse;
156156
margin-bottom: $verticalSpacingOutside;
157+
align-items: end;
158+
}
159+
&-toolbar {
160+
padding: $spacingInside;
161+
min-height: 2em;
162+
flex-grow: 1;
163+
display: flex;
164+
align-items: center;
165+
flex-wrap: wrap;
157166
}
158167
&-search {
159168
padding: $spacingInside;
160169

161170
&-input {
162171
font: inherit;
163-
line-height: 2em;
172+
line-height: 2em;
173+
min-height: 2em;
164174
border: $innerBorder;
165175
border-radius: $borderRadius;
166176
padding: 0 $spacingInside;
@@ -194,6 +204,7 @@
194204
margin-right: -0.75em;
195205
padding: 0 0.25em 0 1.25em;
196206
line-height: 2em;
207+
height: 2em;
197208
text-overflow: ellipsis;
198209

199210
&.has-nested-items {

src/component/Inspector.tsx

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -214,25 +214,31 @@ export class Inspector extends React.Component<
214214
buildArrayProperties,
215215
searchOptions,
216216
breadcrumbs,
217+
renderHeaderToolBar,
217218
renderItemContent,
218219
renderSelectionDetails,
219220
renderEmptyDetails
220221
} = this.props;
221222
const { selectedItems, appendEmptyColumn, enteredSearchFilter, appliedSearchFilter } = this.state;
222-
const { columnData } = this.getRenderDataForSelection(schemas, referenceSchemas, selectedItems, parserConfig, buildArrayProperties);
223+
const renderDataForSelection = this.getRenderDataForSelection(schemas, referenceSchemas, selectedItems, parserConfig, buildArrayProperties);
224+
const { columnData } = renderDataForSelection;
223225
// apply search filter if enabled or clear (potentially left-over) search results
224226
columnData.forEach(this.setFilteredItemsForColumn(searchOptions, appliedSearchFilter));
225227
const searchFeatureEnabled =
226228
searchOptions && (searchOptions.byPropertyName || (searchOptions.fields && searchOptions.fields.length) || searchOptions.filterBy);
229+
const shouldShowHeaderToolBar = renderHeaderToolBar || searchFeatureEnabled;
227230
return (
228231
<div className="jsonschema-inspector">
229-
{searchFeatureEnabled && (
232+
{shouldShowHeaderToolBar && (
230233
<div className="jsonschema-inspector-header">
231-
<InspectorSearchField
232-
searchFilter={enteredSearchFilter}
233-
onSearchFilterChange={this.onSearchFilterChange}
234-
placeholder={searchOptions.inputPlaceholder}
235-
/>
234+
{searchFeatureEnabled && (
235+
<InspectorSearchField
236+
searchFilter={enteredSearchFilter}
237+
onSearchFilterChange={this.onSearchFilterChange}
238+
placeholder={searchOptions.inputPlaceholder}
239+
/>
240+
)}
241+
{renderHeaderToolBar && <div className="jsonschema-inspector-toolbar">{renderHeaderToolBar(renderDataForSelection)}</div>}
236242
</div>
237243
)}
238244
<div className="jsonschema-inspector-body">
@@ -285,6 +291,7 @@ export class Inspector extends React.Component<
285291
debounceMaxWait: PropTypes.number
286292
}),
287293
onSelect: PropTypes.func,
294+
renderHeaderToolBar: PropTypes.func,
288295
renderItemContent: PropTypes.func,
289296
renderSelectionDetails: PropTypes.func,
290297
renderEmptyDetails: PropTypes.func
@@ -303,6 +310,7 @@ export class Inspector extends React.Component<
303310
fields: ["title", "description"]
304311
},
305312
onSelect: undefined,
313+
renderHeaderToolBar: undefined,
306314
renderItemContent: undefined,
307315
renderSelectionDetails: undefined,
308316
renderEmptyDetails: undefined

src/component/InspectorTypes.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,10 @@ export interface InspectorDefaultProps {
104104
newRenderData: { columnData: Array<RenderColumn> },
105105
breadcrumbsTexts?: Array<string>
106106
) => void;
107+
/**
108+
* Custom render function for the header tool-bar (to the left of the search field).
109+
*/
110+
renderHeaderToolBar?: (props: { columnData: Array<RenderColumn> }) => React.ReactElement;
107111
/**
108112
* Custom render function for the content of a single item in a column.
109113
* Expects a single object as input with the following keys:
@@ -125,6 +129,7 @@ export interface InspectorDefaultProps {
125129
* - "itemSchemaGroup": the full `JsonSchemaGroup` associated with the currently selected trailing item (i.e. right-most selection)
126130
* - "columnData": the full render information for all columns
127131
* - "selectionColumnIndex": indicating the index of the right-most column containing a selected item (for convenient use of "columnData")
132+
* - "optionIndexes": in case of an optional path being selected
128133
*/
129134
renderSelectionDetails?: (props: {
130135
itemSchemaGroup: JsonSchemaGroup;
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { Meta, Preview, Story, Props } from '@storybook/addon-docs/blocks';
2+
import { action } from "@storybook/addon-actions";
3+
import "./style-overrides.css";
4+
5+
import { Inspector, getCommonFieldValuesFromSchemaGroup } from "../src/index";
6+
7+
import personSchema from "./schema-person.json";
8+
import shopSelectionSchema from "./schema-shop-selection.json";
9+
10+
<Meta title="Inspector (renderHeaderToolBar)" component={Inspector} />
11+
12+
# Inspector (renderHeaderToolBar)
13+
## Focus: Customise Header Tool-Bar
14+
15+
<Props of={Inspector} exclude={Object.keys(Inspector.propTypes).filter(propName => propName !== "renderHeaderToolBar")} />
16+
17+
----
18+
19+
### with custom tool-bar items
20+
The `renderHeaderToolBar` prop allows you to define additional content to be displayed besides the search filter in the header.
21+
22+
<Preview>
23+
<Story name="with custom tool-bar items">
24+
<Inspector
25+
schemas={{
26+
Person: personSchema,
27+
Shop: shopSelectionSchema
28+
}}
29+
defaultSelectedItems={["Person", "friends"]}
30+
renderHeaderToolBar={({ columnData }) => (
31+
<>
32+
<button type="button" style={{ padding: "0.25em", lineHeight: "2em", marginRight: ".5em" }}>Static</button>
33+
{columnData.filter(column => column.selectedItem).map((column, index) => (
34+
<button key={index} type="button" style={{ padding: "0.25em", lineHeight: "2em" }}>{`#${index}: ${column.selectedItem}`}</button>
35+
))}
36+
</>
37+
)}
38+
onSelect={action("onSelect")}
39+
/>
40+
</Story>
41+
</Preview>

test/component/Inspector.test.tsx

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,35 @@ describe("renders correctly", () => {
162162
expect(filteredItems).toBeUndefined();
163163
});
164164
});
165+
describe("with header tool-bar", () => {
166+
const renderHeaderToolBar = (): React.ReactChild => <div>tool-bar-item</div>;
167+
it("without search field", () => {
168+
const component = shallow(<Inspector schemas={schemas} renderHeaderToolBar={renderHeaderToolBar} searchOptions={null} />);
169+
const header = component.find(".jsonschema-inspector-header");
170+
expect(header.exists()).toBe(true);
171+
expect(header.childAt(0).text()).toBe("tool-bar-item");
172+
expect(header.childAt(1).exists()).toBe(false);
173+
});
174+
it("in addition to search field", () => {
175+
const component = shallow(
176+
<Inspector
177+
schemas={schemas}
178+
renderHeaderToolBar={renderHeaderToolBar}
179+
searchOptions={{
180+
byPropertyName: true
181+
}}
182+
/>
183+
);
184+
const header = component.find(".jsonschema-inspector-header");
185+
expect(header.exists()).toBe(true);
186+
expect(header.childAt(0).prop("searchFilter")).toBe("");
187+
expect(header.childAt(1).text()).toBe("tool-bar-item");
188+
});
189+
});
190+
it("without header", () => {
191+
const component = shallow(<Inspector schemas={schemas} searchOptions={null} />);
192+
expect(component.find(".jsonschema-inspector-header").exists()).toBe(false);
193+
});
165194
it("without footer", () => {
166195
const component = shallow(<Inspector schemas={schemas} breadcrumbs={null} />);
167196
expect(component.find(".jsonschema-inspector-footer").exists()).toBe(false);

0 commit comments

Comments
 (0)