Skip to content

Commit

Permalink
Add 'auto' as placement option (#3009)
Browse files Browse the repository at this point in the history
* Add 'auto' as placement option

* Add auto positions to usage page
  • Loading branch information
patrikholcak authored Oct 25, 2024
1 parent a6e31b4 commit bce520c
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 7 deletions.
2 changes: 1 addition & 1 deletion docs-src/src/content/docs/guides/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ created.
- `attachTo`: The element the step should be attached to on the page. An object with properties `element` and `on`.
- `element`: An element selector string, a DOM element, or a function (returning a selector, a DOM element, `null` or `undefined`).
- `on`: The optional direction to place the Floating UI tooltip relative to the element.
- Possible string values: 'top', 'top-start', 'top-end', 'bottom', 'bottom-start', 'bottom-end', 'right', 'right-start', 'right-end', 'left', 'left-start', 'left-end'
- Possible string values: 'auto', 'auto-start', 'auto-end', 'top', 'top-start', 'top-end', 'bottom', 'bottom-start', 'bottom-end', 'right', 'right-start', 'right-end', 'left', 'left-start', 'left-end'

```js
const new Step(tour, {
Expand Down
3 changes: 3 additions & 0 deletions shepherd.js/src/step.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,9 @@ export interface StepOptions {
}

export type PopperPlacement =
| 'auto'
| 'auto-start'
| 'auto-end'
| 'top'
| 'top-start'
| 'top-end'
Expand Down
29 changes: 23 additions & 6 deletions shepherd.js/src/utils/floating-ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import {
arrow,
computePosition,
flip,
autoPlacement,
limitShift,
shift,
type ComputePositionConfig,
type MiddlewareData,
type Placement
type Placement,
type Alignment
} from '@floating-ui/dom';
import type { Step, StepOptions, StepOptionsAttachTo } from '../step.ts';
import { isHTMLElement } from './type-check.ts';
Expand Down Expand Up @@ -180,9 +182,27 @@ export function getFloatingUIOptions(

const shouldCenter = shouldCenterStep(attachToOptions);

const hasAutoPlacement = attachToOptions.on?.includes('auto');

const hasEdgeAlignment =
attachToOptions?.on?.includes('-start') ||
attachToOptions?.on?.includes('-end');

if (!shouldCenter) {
if (hasAutoPlacement) {
options.middleware.push(
autoPlacement({
crossAxis: true,
alignment: hasEdgeAlignment
? (attachToOptions?.on?.split('-').pop() as Alignment)
: null
})
);
} else {
options.middleware.push(flip());
}

options.middleware.push(
flip(),
// Replicate PopperJS default behavior.
shift({
limiter: limitShift(),
Expand All @@ -191,15 +211,12 @@ export function getFloatingUIOptions(
);

if (arrowEl) {
const hasEdgeAlignment =
attachToOptions?.on?.includes('-start') ||
attachToOptions?.on?.includes('-end');
options.middleware.push(
arrow({ element: arrowEl, padding: hasEdgeAlignment ? 4 : 0 })
);
}

options.placement = attachToOptions.on;
if (!hasAutoPlacement) options.placement = attachToOptions.on as Placement;
}

return deepmerge(options, step.options.floatingUIOptions || {});
Expand Down
54 changes: 54 additions & 0 deletions test/unit/tour.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -666,6 +666,60 @@ describe('Tour | Top-Level Class', function () {

expect(stepsContainer.contains(stepElement)).toBe(true);
});

it('adds autoPlacement middleware when attachTo.on is set to auto', () => {
const div = document.createElement('div');
div.classList.add('modifiers-test');
document.body.appendChild(div);
instance = new Shepherd.Tour();

const step1 = instance.addStep({
id: 'test',
title: 'This is a test step for our tour',
attachTo: { element: '.modifiers-test', on: 'auto' }
});

const step2 = instance.addStep({
id: 'test',
title: 'This is a test step for our tour',
attachTo: { element: '.modifiers-test', on: 'auto-start' }
});

const step3 = instance.addStep({
id: 'test',
title: 'This is a test step for our tour',
attachTo: { element: '.modifiers-test', on: 'auto-end' }
});

instance.start();

const step1FloatingUIOptions = setupTooltip(step1);
const step1MiddlewareNames = step1FloatingUIOptions.middleware.map(
({ name }) => name
);
const step1PlacementMiddleware = step1FloatingUIOptions.middleware.find(
({ name }) => name === 'autoPlacement'
);
expect(step1MiddlewareNames.includes('autoPlacement')).toBe(true);
expect(step1MiddlewareNames.includes('flip')).toBe(false);
expect(step1PlacementMiddleware.options.alignment).toBe(null);

instance.next();

const step2FloatingUIOptions = setupTooltip(step2);
const step2PlacementMiddleware = step2FloatingUIOptions.middleware.find(
({ name }) => name === 'autoPlacement'
);
expect(step2PlacementMiddleware.options.alignment).toBe('start');

instance.next();

const step3FloatingUIOptions = setupTooltip(step3);
const step3PlacementMiddleware = step3FloatingUIOptions.middleware.find(
({ name }) => name === 'autoPlacement'
);
expect(step3PlacementMiddleware.options.alignment).toBe('end');
});
});

describe('shepherdModalOverlayContainer', function () {
Expand Down

0 comments on commit bce520c

Please sign in to comment.