import classNames from 'classnames';
import * as React from 'react';
-interface Props {
+export interface FacetBoxProps {
className?: string;
children: React.ReactNode;
property: string;
}
-export default function FacetBox(props: Props) {
+export default function FacetBox(props: FacetBoxProps) {
return (
<div
className={classNames('search-navigator-facet-box', props.className)}
export default class FacetHeader extends React.PureComponent<Props> {
handleClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => {
event.preventDefault();
- event.currentTarget.blur();
+ event.nativeEvent.preventDefault();
if (this.props.onClick) {
this.props.onClick();
}
<div className="search-navigator-facet-header-wrapper display-flex-center">
{this.props.onClick ? (
<span className="search-navigator-facet-header display-flex-center">
- <a href="#" onClick={this.handleClick}>
+ <a href="#" onClick={this.handleClick} aria-expanded={this.props.open}>
<OpenCloseIcon className="little-spacer-right" open={this.props.open} />
{this.props.name}
</a>
*/
import * as React from 'react';
-interface Props {
+export interface FacetItemsListProps {
children?: React.ReactNode;
title?: string;
}
-export default function FacetItemsList({ children, title }: Props) {
+export default function FacetItemsList({ children, title }: FacetItemsListProps) {
return (
<div className="search-navigator-facet-list">
{title && <div className="search-navigator-facet-list-title">{title}</div>}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import { screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import * as React from 'react';
+import { renderComponent } from '../../../helpers/testReactTestingUtils';
+import FacetBox, { FacetBoxProps } from '../FacetBox';
+import FacetHeader from '../FacetHeader';
+import FacetItem from '../FacetItem';
+import FacetItemsList, { FacetItemsListProps } from '../FacetItemsList';
+
+it('should render and function correctly', () => {
+ const onFacetClick = jest.fn();
+ renderFacet(undefined, undefined, undefined, { onClick: onFacetClick });
+
+ // Start closed.
+ let facetHeader = screen.getByRole('link', { name: 'foo', expanded: false });
+ expect(facetHeader).toBeInTheDocument();
+ expect(screen.queryByText('Foo/Bar')).not.toBeInTheDocument();
+
+ // Expand.
+ facetHeader.click();
+ facetHeader = screen.getByRole('link', { name: 'foo', expanded: true });
+ expect(facetHeader).toBeInTheDocument();
+ expect(screen.getByText('Foo/Bar')).toBeInTheDocument();
+
+ // Interact with facets.
+ const facet1 = screen.getByRole('link', { name: 'Foo/Bar 10' });
+ expect(facet1).toHaveClass('active');
+ facet1.click();
+ expect(onFacetClick).toBeCalledWith('bar', false);
+
+ const facet2 = screen.getByRole('link', { name: 'Foo/Baz' });
+ expect(facet2).not.toHaveClass('active');
+
+ expect(screen.queryByRole('link', { name: 'Foo/Bat' })).not.toBeInTheDocument();
+ expect(screen.getByText('Foo/Bat')).toBeInTheDocument();
+
+ // Collapse again.
+ facetHeader.click();
+ expect(screen.getByRole('link', { name: 'foo', expanded: false })).toBeInTheDocument();
+ expect(screen.queryByText('Foo/Bar')).not.toBeInTheDocument();
+});
+
+it('should correctly render a header with helper text', async () => {
+ renderFacet(undefined, { helper: 'Help text' });
+ await userEvent.tab();
+ await userEvent.tab();
+ expect(screen.getByText('Help text')).toBeInTheDocument();
+});
+
+it('should correctly render a header with value data', () => {
+ renderFacet(undefined, { values: ['value 1'] });
+ expect(screen.getByText('value 1')).toBeInTheDocument();
+ screen.getByRole('button', { name: 'clear' }).click();
+ expect(screen.queryByText('value 1')).not.toBeInTheDocument();
+});
+
+it('should correctly render a disabled header', () => {
+ renderFacet(undefined, { onClick: undefined });
+ expect(screen.queryByRole('link', { name: 'foo' })).not.toBeInTheDocument();
+});
+
+it('should correctly render a facet item list with title', () => {
+ renderFacet(undefined, { open: true }, { title: 'My list title' });
+ expect(screen.getByText('My list title')).toBeInTheDocument();
+});
+
+function renderFacet(
+ facetBoxProps: Partial<FacetBoxProps> = {},
+ facetHeaderProps: Partial<FacetHeader['props']> = {},
+ facetItemListProps: Partial<FacetItemsListProps> = {},
+ facetItemProps: Partial<FacetItem['props']> = {}
+) {
+ function Facet() {
+ const [open, setOpen] = React.useState(facetHeaderProps.open ?? false);
+ const [values, setValues] = React.useState(facetHeaderProps.values ?? undefined);
+
+ return (
+ <FacetBox property="foo" {...facetBoxProps}>
+ <FacetHeader
+ name="foo"
+ onClick={() => setOpen(!open)}
+ onClear={() => setValues(undefined)}
+ {...{ ...facetHeaderProps, open, values }}
+ />
+
+ {open && (
+ <FacetItemsList {...facetItemListProps}>
+ <FacetItem
+ active={true}
+ name="Foo/Bar"
+ onClick={jest.fn()}
+ value="bar"
+ stat={10}
+ {...facetItemProps}
+ />
+ <FacetItem
+ active={false}
+ name="Foo/Baz"
+ onClick={jest.fn()}
+ value="baz"
+ {...facetItemProps}
+ />
+ <FacetItem
+ halfWidth={true}
+ name="Foo/Bat"
+ onClick={jest.fn()}
+ value="bat"
+ disabled={true}
+ {...facetItemProps}
+ />
+ </FacetItemsList>
+ )}
+ </FacetBox>
+ );
+ }
+
+ return renderComponent(<Facet />);
+}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import FacetBox from '../FacetBox';
-
-it('should render', () => {
- expect(
- shallow(
- <FacetBox property="foo">
- <div />
- </FacetBox>
- )
- ).toMatchSnapshot();
-});
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { click } from '../../../helpers/testUtils';
-import FacetHeader from '../FacetHeader';
-
-it('should render open facet with value', () => {
- expect(
- shallow(<FacetHeader name="foo" onClick={jest.fn()} open={true} values={['foo']} />)
- ).toMatchSnapshot();
-});
-
-it('should render open facet without value', () => {
- expect(shallow(<FacetHeader name="foo" onClick={jest.fn()} open={true} />)).toMatchSnapshot();
-});
-
-it('should render closed facet with value', () => {
- expect(
- shallow(<FacetHeader name="foo" onClick={jest.fn()} open={false} values={['foo']} />)
- ).toMatchSnapshot();
-});
-
-it('should render closed facet without value', () => {
- expect(shallow(<FacetHeader name="foo" onClick={jest.fn()} open={false} />)).toMatchSnapshot();
-});
-
-it('should render without link', () => {
- expect(shallow(<FacetHeader name="foo" open={false} />)).toMatchSnapshot();
-});
-
-it('should render with a spinner if loading', () => {
- expect(shallow(<FacetHeader fetching={true} name="foo" open={false} />)).toMatchSnapshot();
-});
-
-it('should call onClick', () => {
- const onClick = jest.fn();
- const wrapper = shallow(<FacetHeader name="foo" onClick={onClick} open={false} />);
- click(wrapper.find('a'));
- expect(onClick).toHaveBeenCalled();
-});
-
-it('should clear', () => {
- const onClear = jest.fn();
- const wrapper = shallow(
- <FacetHeader
- name="foo"
- onClear={onClear}
- onClick={jest.fn()}
- open={false}
- values={['foo', 'bar', 'baz']}
- />
- );
- expect(wrapper).toMatchSnapshot();
- click(wrapper.find('.button-red'));
- expect(onClear).toHaveBeenCalled();
-});
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { click } from '../../../helpers/testUtils';
-import FacetItem, { Props } from '../FacetItem';
-
-it('should render active', () => {
- expect(renderFacetItem({ active: true })).toMatchSnapshot();
-});
-
-it('should render inactive', () => {
- expect(renderFacetItem({ active: false })).toMatchSnapshot();
-});
-
-it('should render stat', () => {
- expect(renderFacetItem({ stat: '13' })).toMatchSnapshot();
-});
-
-it('should render disabled', () => {
- expect(renderFacetItem({ disabled: true })).toMatchSnapshot();
-});
-
-it('should render half width', () => {
- expect(renderFacetItem({ halfWidth: true })).toMatchSnapshot();
-});
-
-it('should call onClick', () => {
- const onClick = jest.fn();
- const wrapper = renderFacetItem({ onClick });
- click(wrapper.find('a'), { currentTarget: { blur() {}, dataset: { value: 'bar' } } });
- expect(onClick).toHaveBeenCalled();
-});
-
-function renderFacetItem(props?: Partial<Props>) {
- return shallow(
- <FacetItem
- active={false}
- name="foo"
- onClick={jest.fn()}
- stat={null}
- tooltip="foo"
- value="bar"
- {...props}
- />
- );
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import FacetItemsList from '../FacetItemsList';
-
-it('should render', () => {
- expect(
- shallow(
- <FacetItemsList>
- <div />
- </FacetItemsList>
- )
- ).toMatchSnapshot();
-});
-
-it('should render with title', () => {
- expect(
- shallow(
- <FacetItemsList title="title test">
- <div />
- </FacetItemsList>
- )
- ).toMatchSnapshot();
-});
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render 1`] = `
-<div
- className="search-navigator-facet-box"
- data-property="foo"
->
- <div />
-</div>
-`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should clear 1`] = `
-<div
- className="search-navigator-facet-header-wrapper display-flex-center"
->
- <span
- className="search-navigator-facet-header display-flex-center"
- >
- <a
- href="#"
- onClick={[Function]}
- >
- <OpenCloseIcon
- className="little-spacer-right"
- open={false}
- />
- foo
- </a>
- </span>
- <span
- className="search-navigator-facet-header-value spacer-left spacer-right "
- >
- <span
- className="badge text-ellipsis"
- title="x_selected.3"
- >
- x_selected.3
- </span>
- </span>
- <Button
- className="search-navigator-facet-header-button button-small button-red"
- onClick={[MockFunction]}
- >
- clear
- </Button>
-</div>
-`;
-
-exports[`should render closed facet with value 1`] = `
-<div
- className="search-navigator-facet-header-wrapper display-flex-center"
->
- <span
- className="search-navigator-facet-header display-flex-center"
- >
- <a
- href="#"
- onClick={[Function]}
- >
- <OpenCloseIcon
- className="little-spacer-right"
- open={false}
- />
- foo
- </a>
- </span>
- <span
- className="search-navigator-facet-header-value spacer-left spacer-right "
- >
- <span
- className="badge text-ellipsis"
- title="foo"
- >
- foo
- </span>
- </span>
-</div>
-`;
-
-exports[`should render closed facet without value 1`] = `
-<div
- className="search-navigator-facet-header-wrapper display-flex-center"
->
- <span
- className="search-navigator-facet-header display-flex-center"
- >
- <a
- href="#"
- onClick={[Function]}
- >
- <OpenCloseIcon
- className="little-spacer-right"
- open={false}
- />
- foo
- </a>
- </span>
- <span
- className="search-navigator-facet-header-value spacer-left spacer-right "
- />
-</div>
-`;
-
-exports[`should render open facet with value 1`] = `
-<div
- className="search-navigator-facet-header-wrapper display-flex-center"
->
- <span
- className="search-navigator-facet-header display-flex-center"
- >
- <a
- href="#"
- onClick={[Function]}
- >
- <OpenCloseIcon
- className="little-spacer-right"
- open={true}
- />
- foo
- </a>
- </span>
- <span
- className="search-navigator-facet-header-value spacer-left spacer-right "
- >
- <span
- className="badge text-ellipsis"
- title="foo"
- >
- foo
- </span>
- </span>
-</div>
-`;
-
-exports[`should render open facet without value 1`] = `
-<div
- className="search-navigator-facet-header-wrapper display-flex-center"
->
- <span
- className="search-navigator-facet-header display-flex-center"
- >
- <a
- href="#"
- onClick={[Function]}
- >
- <OpenCloseIcon
- className="little-spacer-right"
- open={true}
- />
- foo
- </a>
- </span>
- <span
- className="search-navigator-facet-header-value spacer-left spacer-right "
- />
-</div>
-`;
-
-exports[`should render with a spinner if loading 1`] = `
-<div
- className="search-navigator-facet-header-wrapper display-flex-center"
->
- <span
- className="search-navigator-facet-header display-flex-center"
- >
- foo
- </span>
- <span
- className="search-navigator-facet-header-value spacer-left spacer-right "
- />
- <span
- className="little-spacer-right"
- >
- <DeferredSpinner />
- </span>
-</div>
-`;
-
-exports[`should render without link 1`] = `
-<div
- className="search-navigator-facet-header-wrapper display-flex-center"
->
- <span
- className="search-navigator-facet-header display-flex-center"
- >
- foo
- </span>
- <span
- className="search-navigator-facet-header-value spacer-left spacer-right "
- />
-</div>
-`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render active 1`] = `
-<a
- className="search-navigator-facet active"
- data-facet="bar"
- href="#"
- onClick={[Function]}
- title="foo"
->
- <span
- className="facet-name"
- >
- foo
- </span>
-</a>
-`;
-
-exports[`should render disabled 1`] = `
-<span
- className="search-navigator-facet"
- data-facet="bar"
- title="foo"
->
- <span
- className="facet-name"
- >
- foo
- </span>
-</span>
-`;
-
-exports[`should render half width 1`] = `
-<a
- className="search-navigator-facet search-navigator-facet-half"
- data-facet="bar"
- href="#"
- onClick={[Function]}
- title="foo"
->
- <span
- className="facet-name"
- >
- foo
- </span>
-</a>
-`;
-
-exports[`should render inactive 1`] = `
-<a
- className="search-navigator-facet"
- data-facet="bar"
- href="#"
- onClick={[Function]}
- title="foo"
->
- <span
- className="facet-name"
- >
- foo
- </span>
-</a>
-`;
-
-exports[`should render stat 1`] = `
-<a
- className="search-navigator-facet"
- data-facet="bar"
- href="#"
- onClick={[Function]}
- title="foo"
->
- <span
- className="facet-name"
- >
- foo
- </span>
- <span
- className="facet-stat"
- >
- 13
- </span>
-</a>
-`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render 1`] = `
-<div
- className="search-navigator-facet-list"
->
- <div />
-</div>
-`;
-
-exports[`should render with title 1`] = `
-<div
- className="search-navigator-facet-list"
->
- <div
- className="search-navigator-facet-list-title"
- >
- title test
- </div>
- <div />
-</div>
-`;