@truedat/ie 4.44.2 → 4.44.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.44.5] 2022-05-20
4
+
5
+ ### Changed
6
+
7
+ - [TD-4230] Refactored `IngestForm` to use `SelectableDynamicForm`
8
+
3
9
  ## [4.37.4] 2022-02-04
4
10
 
5
11
  ### Changed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@truedat/ie",
3
- "version": "4.44.2",
3
+ "version": "4.44.5",
4
4
  "description": "Truedat Web Ingests",
5
5
  "sideEffects": false,
6
6
  "jsnext:main": "src/index.js",
@@ -16,22 +16,26 @@
16
16
  "scripts": {
17
17
  "clean": "rimraf yarn-error.log",
18
18
  "debug": "node --inspect-brk node_modules/.bin/jest --runInBand",
19
- "test": "jest --coverage",
20
- "test:watch": "jest --watch",
19
+ "test": "TZ=UTC jest --coverage",
20
+ "test:watch": "TZ=UTC jest --watch",
21
21
  "eslint": "eslint src/**",
22
22
  "eslint:fix": "eslint --fix src/**"
23
23
  },
24
24
  "devDependencies": {
25
- "@babel/cli": "^7.14.8",
26
- "@babel/core": "^7.15.0",
27
- "@babel/plugin-proposal-class-properties": "^7.14.5",
28
- "@babel/plugin-proposal-object-rest-spread": "^7.14.7",
25
+ "@babel/cli": "^7.17.10",
26
+ "@babel/core": "^7.18.0",
27
+ "@babel/plugin-proposal-class-properties": "^7.17.12",
28
+ "@babel/plugin-proposal-object-rest-spread": "^7.18.0",
29
+ "@babel/plugin-proposal-optional-chaining": "^7.17.12",
29
30
  "@babel/plugin-syntax-dynamic-import": "^7.8.3",
30
- "@babel/plugin-transform-modules-commonjs": "^7.15.0",
31
- "@babel/preset-env": "^7.15.0",
32
- "@babel/preset-react": "^7.14.5",
33
- "@truedat/test": "4.44.2",
34
- "babel-jest": "^27.0.6",
31
+ "@babel/plugin-transform-modules-commonjs": "^7.18.0",
32
+ "@babel/preset-env": "^7.18.0",
33
+ "@babel/preset-react": "^7.17.12",
34
+ "@testing-library/jest-dom": "^5.16.4",
35
+ "@testing-library/react": "^12.0.0",
36
+ "@testing-library/user-event": "^13.2.1",
37
+ "@truedat/test": "4.44.5",
38
+ "babel-jest": "^28.1.0",
35
39
  "babel-plugin-dynamic-import-node": "^2.3.3",
36
40
  "babel-plugin-lodash": "^3.3.4",
37
41
  "babel-plugin-react-intl": "^5.1.18",
@@ -40,7 +44,8 @@
40
44
  "enzyme-adapter-react-16": "^1.15.6",
41
45
  "enzyme-to-json": "^3.6.2",
42
46
  "identity-obj-proxy": "^3.0.0",
43
- "jest": "^27.0.6",
47
+ "jest": "^28.1.0",
48
+ "jest-environment-jsdom": "^28.1.0",
44
49
  "react": "^16.14.0",
45
50
  "react-dom": "^16.14.0",
46
51
  "redux-saga-test-plan": "^4.0.4",
@@ -48,6 +53,8 @@
48
53
  "semantic-ui-react": "^2.0.3"
49
54
  },
50
55
  "jest": {
56
+ "maxWorkers": "50%",
57
+ "testTimeout": 10000,
51
58
  "moduleDirectories": [
52
59
  "<rootDir>/src",
53
60
  "../../node_modules"
@@ -80,14 +87,14 @@
80
87
  ]
81
88
  },
82
89
  "dependencies": {
83
- "@truedat/core": "4.44.2",
84
- "@truedat/df": "4.44.2",
90
+ "@truedat/core": "4.44.5",
91
+ "@truedat/df": "4.44.5",
85
92
  "file-saver": "^2.0.5",
86
93
  "moment": "^2.24.0",
87
94
  "path-to-regexp": "^1.7.0",
88
- "prop-types": "^15.7.2",
95
+ "prop-types": "^15.8.1",
89
96
  "react-intl": "^5.20.10",
90
- "react-moment": "^0.9.7",
97
+ "react-moment": "^1.1.2",
91
98
  "react-redux": "^7.2.4",
92
99
  "react-router-dom": "^5.2.0",
93
100
  "redux": "^4.1.1",
@@ -100,5 +107,5 @@
100
107
  "react-dom": ">= 16.8.6 < 17",
101
108
  "semantic-ui-react": ">= 0.88.2 < 2.1"
102
109
  },
103
- "gitHead": "ca0c5fffcba96736f7a2054f3c37789da8c30a9e"
110
+ "gitHead": "5a339468198c803592b285eddd0dd0c0b0eced93"
104
111
  }
@@ -1,13 +1,9 @@
1
- import React from "react";
2
1
  import { connect } from "react-redux";
3
2
  import { bindActionCreators } from "redux";
3
+ import { AvailableFilters } from "@truedat/core/components";
4
4
  import { addIngestFilter, resetIngestFilters } from "../routines";
5
5
  import { getIngestAvailableFilters } from "../selectors";
6
6
 
7
- const AvailableFilters = React.lazy(() =>
8
- import("@truedat/core/components/AvailableFilters")
9
- );
10
-
11
7
  const mapStateToProps = (state) => ({
12
8
  filters: getIngestAvailableFilters(state),
13
9
  disabled: state.ingestFiltersLoading,
@@ -1,14 +1,10 @@
1
1
  import _ from "lodash/fp";
2
- import React from "react";
3
2
  import { connect } from "react-redux";
4
3
  import { bindActionCreators } from "redux";
4
+ import { AvailableFilters } from "@truedat/core/components";
5
5
  import { addIngestFilter, resetIngestFilters } from "../routines";
6
6
  import { getIngestAvailableFilters } from "../selectors";
7
7
 
8
- const AvailableFilters = React.lazy(() =>
9
- import("@truedat/core/components/AvailableFilters")
10
- );
11
-
12
8
  const mapStateToProps = (state) => ({
13
9
  filters: _.flow(getIngestAvailableFilters, _.without(["status"]))(state),
14
10
  disabled: state.ingestFiltersLoading,
@@ -14,25 +14,17 @@ import { compose } from "redux";
14
14
  import { connect } from "react-redux";
15
15
  import { injectIntl, FormattedMessage } from "react-intl";
16
16
  import { HistoryBackButton } from "@truedat/core/components";
17
- import { selectTemplate, selectDomain } from "@truedat/df/routines";
18
- import { applyTemplate } from "@truedat/df/utils";
19
17
  import { ingestAction } from "../routines";
20
18
 
21
19
  const DomainDropdownSelector = React.lazy(() =>
22
20
  import("@truedat/bg/taxonomy/components/DomainDropdownSelector")
23
21
  );
24
- const DynamicForm = React.lazy(() =>
25
- import("@truedat/df/components/DynamicForm")
22
+ const SelectableDynamicForm = React.lazy(() =>
23
+ import("@truedat/df/components/SelectableDynamicForm")
26
24
  );
27
25
  const RichTextEditor = React.lazy(() =>
28
26
  import("@truedat/core/components/RichTextEditor")
29
27
  );
30
- const TemplateLoader = React.lazy(() =>
31
- import("@truedat/df/templates/components/TemplateLoader")
32
- );
33
- const TemplateSelector = React.lazy(() =>
34
- import("@truedat/df/templates/components/TemplateSelector")
35
- );
36
28
 
37
29
  const actionKey = "create";
38
30
 
@@ -49,59 +41,25 @@ const isValid = _.conforms({
49
41
  name: isNonEmptyString,
50
42
  domain_id: _.isFinite,
51
43
  description: _.negate(_.isEmpty),
44
+ type: isNonEmptyString,
52
45
  });
53
46
 
54
47
  export class IngestForm extends React.Component {
55
48
  static propTypes = {
56
49
  action: PropTypes.object,
57
- applyTemplate: PropTypes.func,
58
50
  ingestAction: PropTypes.func,
59
51
  ingestActionLoading: PropTypes.string,
60
52
  intl: PropTypes.object,
61
- selectDomain: PropTypes.func,
62
- selectTemplate: PropTypes.func,
63
- template: PropTypes.object,
64
- templateLoading: PropTypes.bool,
65
- templates: PropTypes.array,
66
53
  };
67
54
 
68
55
  state = initialState;
69
56
 
70
- componentDidMount() {
71
- const { templates } = this.props;
72
- if (_.size(templates) == 1) {
73
- const { selectTemplate } = this.props;
74
- const id = _.flow(_.head, _.prop("id"))(templates);
75
- selectTemplate({ id });
76
- }
77
- }
78
-
79
- componentDidUpdate() {
80
- const { applyTemplate, template } = this.props;
81
- const { type, content } = this.state;
82
-
83
- if (_.has("name")(template) && !_.propEq("name", type)(template))
84
- this.setState({
85
- type: _.prop("name")(template),
86
- content: applyTemplate(content),
87
- });
88
- }
89
-
90
- handleDomainSelected = (e, { value }) => {
57
+ handleDomainSelected = (_e, { value }) => {
91
58
  const domain_id = value;
92
59
 
93
- const { selectDomain } = this.props;
94
- selectDomain({ id: domain_id });
95
-
96
60
  this.setState({ domain_id });
97
61
  };
98
62
 
99
- handleTemplateSelected = (e, data) => {
100
- const { value } = data;
101
- const { selectTemplate } = this.props;
102
- selectTemplate({ id: value });
103
- };
104
-
105
63
  handleEditorChange = (e, { value }) => {
106
64
  this.handleChange(null, { name: "description", value });
107
65
  };
@@ -114,26 +72,24 @@ export class IngestForm extends React.Component {
114
72
  }
115
73
  };
116
74
 
117
- handleContentChange = (content) => this.setState({ content });
75
+ handleContentChange = ({ content }) => this.setState({ content });
118
76
 
119
77
  isInvalid = () => _.negate(isValid)(this.state);
120
78
 
121
79
  handleSubmit = (e) => {
122
80
  e.preventDefault();
123
- const { action, applyTemplate, ingestAction } = this.props;
124
- const ingestVersion = _.pick(["domain_id", "type", ...staticFields])(
125
- this.state
126
- );
127
-
128
- const content = applyTemplate(
129
- this.state?.content,
130
- ingestVersion?.domain_id
131
- );
81
+ const { action, ingestAction } = this.props;
82
+ const ingest_version = _.pick([
83
+ "domain_id",
84
+ "type",
85
+ "content",
86
+ ...staticFields,
87
+ ])(this.state);
132
88
 
133
89
  ingestAction({
134
90
  action: actionKey,
135
91
  ...action,
136
- ingest_version: { ...ingestVersion, content },
92
+ ingest_version,
137
93
  });
138
94
  };
139
95
 
@@ -142,16 +98,12 @@ export class IngestForm extends React.Component {
142
98
  action,
143
99
  ingestActionLoading,
144
100
  intl: { formatMessage },
145
- template,
146
- templateLoading,
147
- templates,
148
101
  } = this.props;
149
102
 
150
- const { name, description, content } = this.state;
103
+ const { name, description, content, type, domain_id } = this.state;
151
104
 
152
105
  const loading =
153
- templateLoading ||
154
- (action && action.href && ingestActionLoading === action.href);
106
+ action && action.href && ingestActionLoading === action.href;
155
107
 
156
108
  return (
157
109
  <Container as={Segment} text>
@@ -161,18 +113,11 @@ export class IngestForm extends React.Component {
161
113
  <FormattedMessage id="ingests.actions.create" />
162
114
  </Header.Content>
163
115
  </Header>
164
- <TemplateLoader />
165
116
  <Form loading={loading}>
166
117
  <DomainDropdownSelector
167
118
  onChange={this.handleDomainSelected}
168
- invalid={!_.isFinite(this.state.domain_id)}
119
+ invalid={!_.isFinite(domain_id)}
169
120
  />
170
- {_.size(templates) > 1 && (
171
- <TemplateSelector
172
- selectedValue={_.prop("id")(template)}
173
- onChange={this.handleTemplateSelected}
174
- />
175
- )}
176
121
  <Form.Field required>
177
122
  <label>
178
123
  <FormattedMessage id="ingests.props.name" />
@@ -184,7 +129,6 @@ export class IngestForm extends React.Component {
184
129
  </label>
185
130
  <Form.Input name="name" value={name} onChange={this.handleChange} />
186
131
  </Form.Field>
187
-
188
132
  <Form.Field required>
189
133
  <label>
190
134
  <FormattedMessage id="ingests.props.description" />
@@ -200,12 +144,16 @@ export class IngestForm extends React.Component {
200
144
  onChange={this.handleEditorChange}
201
145
  />
202
146
  </Form.Field>
203
- {template && template.id && (
204
- <DynamicForm
205
- onChange={this.handleContentChange}
206
- content={content}
207
- />
208
- )}
147
+
148
+ <SelectableDynamicForm
149
+ scope="ie"
150
+ domainIds={_.isNil(domain_id) ? null : [domain_id]}
151
+ required
152
+ content={content}
153
+ name={type}
154
+ onChange={this.handleContentChange}
155
+ onNameChange={(type) => this.setState({ type })}
156
+ />
209
157
  <div className="actions">
210
158
  <HistoryBackButton
211
159
  content={formatMessage({ id: "actions.cancel" })}
@@ -223,22 +171,12 @@ export class IngestForm extends React.Component {
223
171
  }
224
172
  }
225
173
 
226
- const mapStateToProps = ({
227
- ingestActionLoading,
228
- ingestsActions,
229
- template,
230
- templateLoading,
231
- templates,
232
- }) => ({
174
+ const mapStateToProps = ({ ingestActionLoading, ingestsActions }) => ({
233
175
  action: _.prop(actionKey)(ingestsActions),
234
- applyTemplate: applyTemplate(template),
235
176
  ingestActionLoading,
236
- template,
237
- templateLoading,
238
- templates,
239
177
  });
240
178
 
241
179
  export default compose(
242
180
  injectIntl,
243
- connect(mapStateToProps, { ingestAction, selectTemplate, selectDomain })
181
+ connect(mapStateToProps, { ingestAction })
244
182
  )(IngestForm);
@@ -18,7 +18,7 @@ import {
18
18
  INGESTS,
19
19
  INGESTS_PENDING,
20
20
  INGESTS_NEW,
21
- INGEST_EXECUTIONS
21
+ INGEST_EXECUTIONS,
22
22
  } from "@truedat/core/routes";
23
23
  import IngestRelationsRoutes from "../relations/components/IngestRelationsRoutes";
24
24
  import Events from "./Events";
@@ -77,7 +77,7 @@ const IngestRoutes = ({ ingestLoaded, ingest_id, templatesLoaded }) => {
77
77
  <>
78
78
  <IngestsLoader
79
79
  defaultFilters={{
80
- status: ["draft", "pending_approval", "rejected"]
80
+ status: ["draft", "pending_approval", "rejected"],
81
81
  }}
82
82
  />
83
83
  <IngestFiltersLoader />
@@ -92,9 +92,8 @@ const IngestRoutes = ({ ingestLoaded, ingest_id, templatesLoaded }) => {
92
92
  <>
93
93
  <DomainsLoader actions="create_ingest" />
94
94
  <IngestCrumbs ingestAction="ingests.actions.create" />
95
- <TemplatesLoader scope="ie" />
96
95
  <IngestsLoader />
97
- {templatesLoaded && <IngestForm />}
96
+ <IngestForm />
98
97
  </>
99
98
  )}
100
99
  />
@@ -191,7 +190,10 @@ const IngestRoutes = ({ ingestLoaded, ingest_id, templatesLoaded }) => {
191
190
  <RelationTagsLoader />
192
191
  <IngestArchiveLoader />
193
192
  {ingestLoaded && (
194
- <EventsLoader resource_id={ingest_id} resource_type="ingest"/>
193
+ <EventsLoader
194
+ resource_id={ingest_id}
195
+ resource_type="ingest"
196
+ />
195
197
  )}
196
198
  {ingestLoaded && <Ingest />}
197
199
  {ingestLoaded && <Events />}
@@ -283,13 +285,13 @@ const IngestRoutes = ({ ingestLoaded, ingest_id, templatesLoaded }) => {
283
285
  IngestRoutes.propTypes = {
284
286
  ingestLoaded: PropTypes.bool,
285
287
  templatesLoaded: PropTypes.bool,
286
- ingest_id: PropTypes.number
288
+ ingest_id: PropTypes.number,
287
289
  };
288
290
 
289
291
  const mapStateToProps = ({ ingest, templatesLoading, templates }) => ({
290
292
  ingestLoaded: !_.isEmpty(ingest),
291
293
  ingest_id: !_.isEmpty(ingest) ? ingest.ingest_id : undefined,
292
- templatesLoaded: !templatesLoading && !_.isEmpty(templates)
294
+ templatesLoaded: !templatesLoading && !_.isEmpty(templates),
293
295
  });
294
296
 
295
297
  export default connect(mapStateToProps)(IngestRoutes);
@@ -1,17 +1,16 @@
1
1
  import React from "react";
2
- import { shallowWithIntl } from "@truedat/test/intl-stub";
3
- import { IngestFilters } from "../IngestFilters";
2
+ import { render } from "@truedat/test/render";
3
+ import IngestFilters from "../IngestFilters";
4
4
 
5
- describe("<IngestFilters />", () => {
6
- const active = true;
7
- const ingestSelectedFilters = {
8
- selectedFilters: [],
9
- openFilter: undefined
10
- };
5
+ const state = {
6
+ ingestFilters: { domain_id: [1, 2], confidential: ["yes", "no"] },
7
+ ingestActiveFilters: { domain_id: [1] },
8
+ };
9
+ const renderOpts = { state };
11
10
 
11
+ describe("<IngestFilters />", () => {
12
12
  it("matches the latest snapshot", () => {
13
- const props = { active, ingestSelectedFilters };
14
- const wrapper = shallowWithIntl(<IngestFilters {...props} />);
15
- expect(wrapper).toMatchSnapshot();
13
+ const { container } = render(<IngestFilters />, renderOpts);
14
+ expect(container).toMatchSnapshot();
16
15
  });
17
16
  });
@@ -1,71 +1,69 @@
1
1
  import React from "react";
2
- import { shallowWithIntl } from "@truedat/test/intl-stub";
3
- import { IngestForm } from "../IngestForm";
2
+ import { render } from "@truedat/test/render";
3
+ import { waitFor } from "@testing-library/react";
4
+ import { multipleTemplatesMock, singleTemplateMock } from "@truedat/test/mocks";
5
+ import IngestForm from "../IngestForm";
4
6
 
5
- describe("<IngestForm />", () => {
6
- const action = {};
7
- const domains = [
8
- { id: 1, name: "Domain1" },
9
- { id: 2, name: "Domain 2" }
10
- ];
11
- const ingestAction = jest.fn();
12
- const ingestActionLoading = "some action";
13
- const template1 = { id: 1, name: "template1" };
14
- const template2 = { id: 2, name: "template2" };
15
- const templateLoading = false;
16
-
17
- const props = {
18
- action,
19
- domains,
20
- ingestAction,
21
- ingestActionLoading,
22
- templateLoading
23
- };
7
+ const variables = { scope: "ie", domainIds: [1] };
24
8
 
9
+ describe("<IngestForm />", () => {
25
10
  describe("with multiple templates", () => {
26
- const templates = [template1, template2];
27
- const selectTemplate = jest.fn();
28
- const wrapper = shallowWithIntl(
29
- <IngestForm
30
- templates={templates}
31
- selectTemplate={selectTemplate}
32
- {...props}
33
- />
34
- );
11
+ const renderOpts = {
12
+ mocks: [multipleTemplatesMock(variables)],
13
+ state: {
14
+ ingestActions: { create: {} },
15
+ ingestActionLoading: "",
16
+ domains: [{ id: 1, name: "domain1" }],
17
+ },
18
+ fallback: "lazy",
19
+ };
35
20
 
36
- it("matches the latest snapshot", () => {
37
- expect(wrapper).toMatchSnapshot();
21
+ it("matches the latest snapshot", async () => {
22
+ const { container, queryByText } = render(<IngestForm />, renderOpts);
23
+ await waitFor(() =>
24
+ expect(queryByText(/loading/i)).not.toBeInTheDocument()
25
+ );
26
+ await waitFor(() => expect(queryByText(/lazy/i)).not.toBeInTheDocument());
27
+ expect(container).toMatchSnapshot();
38
28
  });
39
29
 
40
- it("dispatches selectTemplate when template is selected", () => {
41
- const value = 2;
42
- expect(selectTemplate.mock.calls.length).toBe(0);
43
- wrapper
44
- .findWhere(n => n.prop("onChange"))
45
- .at(1)
46
- .prop("onChange")({}, { value });
47
- expect(selectTemplate.mock.calls.length).toBe(1);
30
+ it("contains a template selector", async () => {
31
+ const { queryByText } = render(<IngestForm />, renderOpts);
32
+ await waitFor(() =>
33
+ expect(queryByText(/loading/i)).not.toBeInTheDocument()
34
+ );
35
+ await waitFor(() => expect(queryByText(/lazy/i)).not.toBeInTheDocument());
36
+ expect(queryByText("template1")).toBeInTheDocument();
48
37
  });
49
38
  });
50
39
 
51
40
  describe("with a single template", () => {
52
- const templates = [template1];
53
- const selectTemplate = jest.fn();
54
- const wrapper = shallowWithIntl(
55
- <IngestForm
56
- templates={templates}
57
- selectTemplate={selectTemplate}
58
- {...props}
59
- />
60
- );
41
+ const renderOpts = {
42
+ mocks: [singleTemplateMock(variables)],
43
+ state: {
44
+ ingestActions: { create: {} },
45
+ ingestActionLoading: "",
46
+ domains: [{ id: 1, name: "domain1" }],
47
+ },
48
+ fallback: "lazy",
49
+ };
61
50
 
62
- it("matches the latest snapshot", () => {
63
- expect(wrapper).toMatchSnapshot();
51
+ it("matches the latest snapshot", async () => {
52
+ const { container, queryByText } = render(<IngestForm />, renderOpts);
53
+ await waitFor(() =>
54
+ expect(queryByText(/loading/i)).not.toBeInTheDocument()
55
+ );
56
+ await waitFor(() => expect(queryByText(/lazy/i)).not.toBeInTheDocument());
57
+ expect(container).toMatchSnapshot();
64
58
  });
65
59
 
66
- it("dispatches selectTemplate when component mounts", () => {
67
- shallowWithIntl(<IngestForm {...props} />);
68
- expect(selectTemplate.mock.calls.length).toBe(1);
60
+ it("contains no <TemplateSelector />", async () => {
61
+ const { queryByText } = render(<IngestForm />, renderOpts);
62
+ await waitFor(() =>
63
+ expect(queryByText(/loading/i)).not.toBeInTheDocument()
64
+ );
65
+ await waitFor(() => expect(queryByText(/lazy/i)).not.toBeInTheDocument());
66
+ expect(queryByText("template1")).not.toBeInTheDocument();
69
67
  });
70
68
  });
71
69
  });
@@ -1,12 +1,12 @@
1
1
  import React from "react";
2
- import { shallowWithIntl } from "@truedat/test/intl-stub";
2
+ import { render } from "@truedat/test/render";
3
3
  import { IngestsLabelResults } from "../IngestsLabelResults";
4
4
 
5
5
  describe("<IngestsLabelResults />", () => {
6
6
  const props = { ingestCount: 22 };
7
7
 
8
8
  it("matches the latest snapshot", () => {
9
- const wrapper = shallowWithIntl(<IngestsLabelResults {...props} />);
10
- expect(wrapper).toMatchSnapshot();
9
+ const { container } = render(<IngestsLabelResults {...props} />);
10
+ expect(container).toMatchSnapshot();
11
11
  });
12
12
  });
@@ -1,14 +1,47 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
3
  exports[`<IngestFilters /> matches the latest snapshot 1`] = `
4
- <Switch>
5
- <Route
6
- exact={true}
7
- path="/ingests"
8
- render={[Function]}
9
- />
10
- <Route
11
- render={[Function]}
12
- />
13
- </Switch>
4
+ <div>
5
+ <div
6
+ aria-expanded="false"
7
+ class="ui button floating labeled scrolling dropdown icon"
8
+ role="listbox"
9
+ tabindex="0"
10
+ >
11
+ <div
12
+ aria-atomic="true"
13
+ aria-live="polite"
14
+ class="divider text"
15
+ role="alert"
16
+ >
17
+ Filters
18
+ </div>
19
+ <i
20
+ aria-hidden="true"
21
+ class="filter icon"
22
+ />
23
+ <div
24
+ class="menu transition"
25
+ >
26
+ <div
27
+ class="item"
28
+ role="option"
29
+ >
30
+ <em>
31
+ (reset all filters)
32
+ </em>
33
+ </div>
34
+ <div
35
+ class="item"
36
+ role="option"
37
+ >
38
+ <span
39
+ class="text"
40
+ >
41
+ confidential
42
+ </span>
43
+ </div>
44
+ </div>
45
+ </div>
46
+ </div>
14
47
  `;