Skip to content

Commit

Permalink
Merge branch 'main' into feat/datagrid/5310
Browse files Browse the repository at this point in the history
  • Loading branch information
cee-chen committed Jan 11, 2022
2 parents 59461c7 + d26becf commit d96203d
Show file tree
Hide file tree
Showing 23 changed files with 722 additions and 93 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,14 @@

- Added the ability to access certain `EuiDataGrid` internal methods via the `ref` prop ([#5499](https://github.com/elastic/eui/pull/5499))

#### END FEATURE BRANCH

- Added virtulized rendering option to `EuiSelectableList` with `isVirtualized` ([#5521](https://github.com/elastic/eui/pull/5521))
- Added expanded option properties to `EuiSelectableOption` with `data` ([#5521](https://github.com/elastic/eui/pull/5521))

**Breaking changes**

- Changed `EuiSearchBar` to preserve phrases with leading and trailing spaces, instead of dropping surrounding whitespace ([#5514](https://github.com/elastic/eui/pull/5514))
- Removed `data-test-subj="dataGridWrapper"` from `EuiDataGrid` in favor of `data-test-subj="euiDataGridBody"` ([#5506](https://github.com/elastic/eui/pull/5506))

## [`44.0.0`](https://github.com/elastic/eui/tree/v44.0.0)
Expand Down
25 changes: 25 additions & 0 deletions cypress/support/commands.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,33 @@
import { mount as cypressMount } from '@cypress/react';
import { React, Fragment } from 'react';
import { EuiProvider } from '../../src';

// Provide global cy.mount() shortcut that includes required providers
// @see https://github.com/cypress-io/cypress/blob/develop/npm/react/docs/providers-and-composition.md
Cypress.Commands.add('mount', (children) => {
return cypressMount(<EuiProvider>{children}</EuiProvider>);
});

// This ensures the correct testing window has focus when using Cypress Real Events.
// @see https://github.com/dmtrKovalenko/cypress-real-events/issues/196
Cypress.Commands.add('realMount', (children) => {
cy.mount(
<Fragment>
<div
data-test-subj="cypress-real-event-target"
style={{ height: '1px', width: '1px' }}
/>
{children}
</Fragment>
).then(() => {
cy.get('[data-test-subj="cypress-real-event-target"]').realClick({
position: 'topLeft',
});
});
});

Cypress.Commands.add('repeatRealPress', (keyToPress, count = 2) => {
for (let i = 0; i < count; i++) {
cy.realPress(keyToPress);
}
});
8 changes: 8 additions & 0 deletions cypress/support/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,13 @@ declare namespace Cypress {

interface Chainable<Subject> {
mount(children: React.ReactNode): Cypress.Chainable<MountReturn>;
realMount(children: React.ReactNode): void;
/**
* Repeat the Real Events `realPress()` method 2 or more times
*
* @param keyToPress Any valid key or array of keys https://docs.cypress.io/api/commands/type#Arguments
* @param count Number of times to invoke `realPress()`. Defaults to 2.
*/
repeatRealPress(keyToPress: string | string[], count?: number): void;
}
}
1 change: 1 addition & 0 deletions cypress/support/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import '@cypress/code-coverage/support';
import './commands.js';
require(THEME_IMPORT); // defined by DefinePlugin in the cypress webpack config
require('cypress-plugin-tab'); // adds the `.tab()` command to cypress chains, see https://docs.cypress.io/api/commands/type#Typing-tab-key-does-not-work
require('cypress-real-events/support'); // uses the Chrome Devtools Protocol to replace simulated events, see https://github.com/dmtrKovalenko/cypress-real-events#why

// @see https://github.com/quasarframework/quasar/issues/2233#issuecomment-492975745
Cypress.on('uncaught:exception', (err) => {
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@
"cssnano": "^4.1.10",
"cypress": "^9.1.1",
"cypress-plugin-tab": "^1.0.5",
"cypress-real-events": "^1.6.0",
"deasync": "^0.1.20",
"dedent": "^0.7.0",
"dts-generator": "^3.0.0",
Expand Down
35 changes: 26 additions & 9 deletions src-docs/src/views/selectable/selectable_custom_render.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { createDataStore } from '../tables/data_store';

export default () => {
const [useCustomContent, setUseCustomContent] = useState(false);
const [isVirtualized, setIsVirtualized] = useState(true);

const countries = createDataStore().countries.map((country) => {
return {
Expand All @@ -20,6 +21,9 @@ export default () => {
prepend: country.flag,
append: <EuiBadge>{country.code}</EuiBadge>,
showIcons: false,
data: {
secondaryContent: 'I am secondary content, I am!',
},
};
});

Expand All @@ -38,49 +42,62 @@ export default () => {
setUseCustomContent(e.currentTarget.checked);
};

const onVirtualized = (e) => {
setIsVirtualized(e.currentTarget.checked);
};

const renderCountryOption = (option, searchValue) => {
return (
<>
<EuiHighlight search={searchValue}>{option.label}</EuiHighlight>
<br />
<EuiTextColor color="subdued">
{/* <br /> */}
<EuiTextColor style={{ display: 'block' }} color="subdued">
<small>
<EuiHighlight search={searchValue}>
I am secondary content, I am!
{option.secondaryContent}
</EuiHighlight>
</small>
</EuiTextColor>
</>
);
};

let listProps = {
isVirtualized,
};

let customProps;
if (useCustomContent) {
customProps = {
height: 240,
renderOption: renderCountryOption,
listProps: {
rowHeight: 50,
showIcons: false,
},
};
listProps = {
rowHeight: 50,
isVirtualized,
};
}

return (
<>
<EuiSwitch
label="Virtualized"
checked={isVirtualized}
onChange={onVirtualized}
/>{' '}
&emsp;
<EuiSwitch
label="Custom content"
checked={useCustomContent}
onChange={onCustom}
/>

<EuiSpacer />

<EuiSelectable
aria-label="Selectable example with custom list items"
searchable
options={options}
onChange={onChange}
listProps={listProps}
{...customProps}
>
{(list, search) => (
Expand Down
30 changes: 23 additions & 7 deletions src-docs/src/views/selectable/selectable_example.js
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,20 @@ export const SelectableExample = {
similar to a title. Add one of these by setting the{' '}
<EuiCode>option.isGroupLabel</EuiCode> to true.{' '}
</p>
<h3>Row height and virtualization</h3>
<p>
When virtualization is on,{' '}
<strong>every row must be the same height</strong> in order for the
list to know how to scroll to the selected or highlighted option. It
applies the <EuiCode>listProps.rowHeight</EuiCode> (in pixels)
directly to each option hiding any overflow.
</p>
<p>
If <EuiCode>listProps.isVirtualized</EuiCode> is set to{' '}
<EuiCode>false</EuiCode>, each row will fit its contents and removes
all scrolling. Therefore, we recommend having a large enough
container to accomodate all optons.
</p>
<h3>Custom content</h3>
<p>
While it is best to stick to the{' '}
Expand All @@ -357,15 +371,17 @@ export const SelectableExample = {
<EuiCode>searchValue</EuiCode> to use for highlighting.
</p>
<p>
In order for the list to know how to scroll to the selected or
highlighted option, it must also know the height of the rows. It
applies this pixel height directly to options. If your custom
content is taller than the default of <EuiCode>32px</EuiCode> tall,
you will need to recalculate this height and apply it via{' '}
<EuiCode>listProps.rowHeight</EuiCode>.
To provide data that can be used by the{' '}
<EuiCode>renderOption</EuiCode> function that does not match the
standard option API, use <EuiCode>option.data</EuiCode> which will
make custom data available in the <EuiCode>option</EuiCode>{' '}
parameter. See the <EuiCode>secondaryContent</EuiCode> configuration
in the following example.
</p>
<p>
<strong>Every row must be the same height.</strong>
Also, if your custom content is taller than the default{' '}
<EuiCode>listProps.rowHeight</EuiCode> of <EuiCode>32px</EuiCode>{' '}
tall, you will need to pass in a custom value to this prop.
</p>
</Fragment>
),
Expand Down
120 changes: 120 additions & 0 deletions src/components/accordion/accordion.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import React from 'react';
import { EuiAccordion, EuiAccordionProps } from './index';
import { EuiPanel } from '../../components/panel';
import { htmlIdGenerator } from '../../services';

const baseProps: EuiAccordionProps = {
buttonContent: 'Click me to toggle',
id: htmlIdGenerator()(),
initialIsOpen: false,
};

const noArrow = { arrowDisplay: 'none' };
const noArrowProps: EuiAccordionProps = Object.assign(baseProps, noArrow);

describe('EuiAccordion', () => {
describe('Keyboard and screen reader accessibility', () => {
it('renders with required props', () => {
cy.realMount(
<EuiAccordion {...baseProps}>
<EuiPanel color="subdued">
Any content inside of <strong>EuiAccordion</strong> will appear
here.
</EuiPanel>
</EuiAccordion>
);
cy.realPress('Tab');
cy.focused().contains('Click me to toggle');
});

it('opens and closes on ENTER keypress', () => {
cy.realMount(
<EuiAccordion {...baseProps}>
<EuiPanel color="subdued">
Any content inside of <strong>EuiAccordion</strong> will appear
here.
</EuiPanel>
</EuiAccordion>
);
cy.realPress('Tab');
cy.focused().contains('Click me to toggle').realPress('Enter');
cy.realPress(['Shift', 'Tab']);
cy.focused().invoke('attr', 'aria-expanded').should('equal', 'true');
cy.realPress('Enter');
cy.focused().invoke('attr', 'aria-expanded').should('equal', 'false');
});

it('opens and closes on SPACE keypress', () => {
cy.realMount(
<EuiAccordion {...baseProps}>
<EuiPanel color="subdued">
Any content inside of <strong>EuiAccordion</strong> will appear
here.
</EuiPanel>
</EuiAccordion>
);
cy.realPress('Tab');
cy.focused().contains('Click me to toggle').realPress('Space');
cy.realPress(['Shift', 'Tab']);
cy.focused().invoke('attr', 'aria-expanded').should('equal', 'true');
cy.realPress('Space');
cy.focused().invoke('attr', 'aria-expanded').should('equal', 'false');
});
});

describe('Props and keyboard navigation', () => {
it('should not have an arrow', () => {
cy.realMount(
<EuiAccordion {...noArrowProps}>
<EuiPanel color="subdued">
Any content inside of <strong>EuiAccordion</strong> will appear
here.
</EuiPanel>
</EuiAccordion>
);
cy.get('.euiAccordion__iconButton').should('not.exist');
});

it('manages focus when panel is opened', () => {
cy.realMount(
<EuiAccordion {...noArrowProps}>
<EuiPanel color="subdued">
Any content inside of <strong>EuiAccordion</strong> will appear
here. We will include <a href="#">a link</a> to confirm focus.
</EuiPanel>
</EuiAccordion>
);
cy.realPress('Tab');
cy.focused().contains('Click me to toggle').realPress('Enter');
cy.focused().invoke('attr', 'tabindex').should('equal', '-1');
cy.focused().contains('Any content inside of EuiAccordion');
cy.realPress('Tab');
cy.focused().contains('a link');
});

it('manages focus when forceState is open', () => {
cy.realMount(
<EuiAccordion {...noArrowProps} forceState="open">
<EuiPanel color="subdued">
Any content inside of <strong>EuiAccordion</strong> will appear
here. We will include <a href="#">a link</a> to confirm focus.
</EuiPanel>
</EuiAccordion>
);
cy.realPress('Tab');
cy.focused().contains('Click me to toggle');
cy.focused().invoke('attr', 'aria-expanded').should('equal', 'true');
cy.focused().invoke('attr', 'tabindex').should('not.exist');
cy.realPress('Tab');
cy.focused().contains('a link');
});
});
});
Loading

0 comments on commit d96203d

Please sign in to comment.