Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

- Updated `testenv` mock for `EuiIcon` to render `aria-label` as text ([#5709](https://github.com/elastic/eui/pull/5709))
- Added `editorChecklist` glyph to `EuiIcon` ([#5705](https://github.com/elastic/eui/pull/5705))
- Added `anchor` prop to `EuiTourStep` to allow for DOM selector attachment ([#5696](https://github.com/elastic/eui/pull/5696))

**Breaking changes**

Expand Down
43 changes: 43 additions & 0 deletions src-docs/src/views/tour/step_dom.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React, { useRef, useState } from 'react';

import {
EuiButtonIcon,
EuiText,
EuiSpacer,
EuiTourStep,
} from '../../../../src/components';

export default () => {
const [isOpen, setIsOpen] = useState(true);
const anchorRef = useRef();
return (
<div>
<EuiTourStep
// anchor={() => anchorRef.current}
anchor="#anchorTarget"
Comment thread
thompsongl marked this conversation as resolved.
Outdated
content={
<EuiText>
<p>The tour step content.</p>
</EuiText>
}
isStepOpen={isOpen}
minWidth={300}
onFinish={() => setIsOpen(false)}
step={1}
stepsTotal={1}
title="Title of the current step"
subtitle="Title of the full tour (optional)"
anchorPosition="rightUp"
/>
<EuiButtonIcon
onClick={() => setIsOpen(!isOpen)}
iconType="globe"
aria-label="Anchor"
buttonRef={anchorRef}
id="anchorTarget"
/>
<EuiSpacer size="xxl" />
<EuiSpacer size="xxl" />
</div>
);
};
22 changes: 22 additions & 0 deletions src-docs/src/views/tour/tour_example.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
} from '../../../../src/components';

import Step from './step';
import StepDom from './step_dom';
import Tour from './tour';
import Managed from './managed';
import ManagedHook from './managed_hook';
Expand Down Expand Up @@ -39,6 +40,7 @@ const stepSnippet = `
</EuiText>
</EuiTourStep>
`;
const stepDomSource = require('!!raw-loader!./step_dom');
const tourSource = require('!!raw-loader!./tour');
const managedSource = require('!!raw-loader!./managed');
const managedHookSource = require('!!raw-loader!./managed_hook');
Expand Down Expand Up @@ -91,6 +93,26 @@ export const TourExample = {
demo: <Step />,
snippet: stepSnippet,
},
{
source: [
{
type: GuideSectionTypes.JS,
code: stepDomSource,
},
],
text: (
<>
<h3>Using DOM selector as anchor location</h3>
<p>
Instead of wrapping the target element, use the{' '}
<EuiCode>anchor</EuiCode> prop to specify a DOM node. Accepted
values include an HTML element or function returning an HTML
element, or a DOM query selector.
Comment thread
thompsongl marked this conversation as resolved.
Outdated
</p>
</>
),
demo: <StepDom />,
},
{
title: 'Standalone steps',
source: [
Expand Down
15 changes: 3 additions & 12 deletions src/components/focus_trap/focus_trap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,9 @@ import { FocusOn } from 'react-focus-on';
import { ReactFocusOnProps } from 'react-focus-on/dist/es5/types';

import { CommonProps } from '../common';
import { findElement, ElementTarget } from '../../services';

/**
* A DOM node, a selector string (which will be passed to
* `document.querySelector()` to find the DOM node), or a function that
* returns a DOM node.
*/
export type FocusTarget = HTMLElement | string | (() => HTMLElement);
export type FocusTarget = ElementTarget;

interface EuiFocusTrapInterface {
/**
Expand Down Expand Up @@ -70,12 +66,7 @@ export class EuiFocusTrap extends Component<EuiFocusTrapProps, State> {

// Programmatically sets focus on a nested DOM node; optional
setInitialFocus = (initialFocus?: FocusTarget) => {
let node = initialFocus instanceof HTMLElement ? initialFocus : null;
if (typeof initialFocus === 'string') {
node = document.querySelector(initialFocus as string);
} else if (typeof initialFocus === 'function') {
node = (initialFocus as () => HTMLElement)();
}
const node = findElement(initialFocus);
if (!node) return;
// `data-autofocus` is part of the 'react-focus-on' API
node.setAttribute('data-autofocus', 'true');
Expand Down
45 changes: 45 additions & 0 deletions src/components/tour/tour_step.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* 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 { EuiTourStep } from './tour_step';

const steps = [
{
step: 1,
content: 'You are here',
},
];

const config = {
onFinish: () => {},
stepsTotal: 1,
title: 'A demo',
};

describe('EuiTourStep', () => {
describe('with an `anchor` configuration', () => {
it('attaches to the anchor element', () => {
cy.realMount(
<>
<span id="anchor">Test</span>
<EuiTourStep
data-test-subj="step"
{...config}
{...steps[0]}
isStepOpen
anchor="#anchor"
/>
</>
);

expect(cy.get('[data-test-subj="step"]').find('#anchor')).to.exist;
});
});
});
Loading