Skip to content

Commit

Permalink
Refactor(web): Improve Tooltip CSS since the position is only contr…
Browse files Browse the repository at this point in the history
…olled by Floating UI #DS-1267
  • Loading branch information
adamkudrna authored and pavelklibani committed Jun 6, 2024
1 parent 1cf89bb commit 0d54664
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 65 deletions.
16 changes: 3 additions & 13 deletions packages/web/src/scss/components/Tooltip/_TooltipPopover.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// 1. Although it may seem pointless, having the arrow as a standalone HTML element has a purpose: the arrow can be
// precisely positioned in sticky tooltips, e.g. with Floating UI:
// 1. The arrow must be a standalone HTML element so the Floating UI library can position it correctly.
// https://floating-ui.com
//
// 2. Reuse already generated custom properties to allow overriding the arrow appearance live in the browser.
Expand All @@ -15,6 +14,7 @@

.TooltipPopover {
@include placement.child();
@include placement.child-controlled($prefix: 'tooltip', $offset: theme.$arrow-height);
@include typography.generate(theme.$typography);

--tooltip-max-width: #{theme.$max-width};
Expand Down Expand Up @@ -72,6 +72,7 @@
$class-name: 'TooltipPopover',
$dictionary-values: theme.$placement-dictionary,
$offset: theme.$arrow-height,
$is-controlled: true,
$has-arrow: true
);

Expand All @@ -83,14 +84,3 @@
.TooltipPopover.is-visible {
@extend %visible;
}

// Controlled placement
// stylelint-disable-next-line selector-max-class
.TooltipPopover.TooltipPopover {
@include placement.child-controlled($prefix: 'tooltip', $offset: theme.$arrow-height);
}

// stylelint-disable-next-line selector-max-class
.TooltipPopover.TooltipPopover .TooltipPopover__arrow {
@include placement.arrow-controlled();
}
64 changes: 62 additions & 2 deletions packages/web/src/scss/tools/__tests__/_dictionaries.test.scss
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,12 @@
@include test.it('should generate correct placement classes based on a dictionary') {
@include test.assert() {
@include test.output() {
@include dictionaries.generate-placements('Test', ('top-start'));
@include dictionaries.generate-placements(
$class-name: 'Test',
$dictionary-values: (
'top-start',
)
);
}

@include test.expect() {
Expand All @@ -141,7 +146,12 @@

@include test.assert() {
@include test.output() {
@include dictionaries.generate-placements('Test', ('right-end'));
@include dictionaries.generate-placements(
$class-name: 'Test',
$dictionary-values: (
'right-end',
)
);
}

@include test.expect() {
Expand All @@ -154,6 +164,56 @@
}
}
}

@include test.assert() {
@include test.output() {
@include dictionaries.generate-placements(
$class-name: 'Test',
$dictionary-values: (
'top-start',
),
$offset: 10px,
$is-controlled: true
);
}

@include test.expect() {
.Test[data-spirit-placement='top-start'] {
--test-offset: 10px;
--test-offset-orthogonal: 0;

transform-origin: bottom left;
}
}
}

@include test.assert() {
@include test.output() {
@include dictionaries.generate-placements(
$class-name: 'Test',
$dictionary-values: (
'top-start',
),
$offset: 10px,
$is-controlled: true,
$has-arrow: true
);
}

@include test.expect() {
.Test[data-spirit-placement='top-start'] {
--test-offset: 10px;
--test-offset-orthogonal: 0;

transform-origin: bottom left;
}

.Test[data-spirit-placement='top-start'] > .Test__arrow {
transform-origin: center;
rotate: z 180deg;
}
}
}
}
}

Expand Down
59 changes: 31 additions & 28 deletions packages/web/src/scss/tools/__tests__/_placement.test.scss
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@
@include test.assert() {
@include test.output() {
.child-test {
@include placement.child(3);
@include placement.child($z-index: 3);
}
}
@include test.expect() {
Expand All @@ -174,11 +174,30 @@
}
}

@include test.it('should apply controlled styles to child') {
@include test.assert() {
@include test.output() {
.child-controlled-test {
@include placement.child-controlled('test', 10px);
}
}
@include test.expect() {
.child-controlled-test {
--test-offset-orthogonal: 0;
--test-offset: 10px;

inset: unset;
translate: unset;
}
}
}
}

@include test.it('should apply custom styles to child variant') {
@include test.assert() {
@include test.output() {
.child-variant-test {
@include placement.child-variant('test', 'top', 10px);
@include placement.child-variant($prefix: 'test', $placement: 'top', $offset: 10px);
}
}
@include test.expect() {
Expand All @@ -191,22 +210,24 @@
}
}
}
}

@include test.it('should apply controlled styles to child') {
@include test.assert() {
@include test.output() {
.child-controlled-test {
@include placement.child-controlled('test', 10px);
.child-variant-test {
@include placement.child-variant(
$prefix: 'test',
$placement: 'top',
$offset: 10px,
$is-controlled: true
);
}
}
@include test.expect() {
.child-controlled-test {
--test-offset-orthogonal: 0;
.child-variant-test {
--test-offset: 10px;
--test-offset-orthogonal: 0;

inset: unset;
translate: unset;
transform-origin: bottom;
}
}
}
Expand Down Expand Up @@ -248,22 +269,4 @@
}
}
}

@include test.it('should apply controlled styles to arrow') {
@include test.assert() {
@include test.output() {
.arrow-controlled-test {
@include placement.arrow-controlled();
}
}

@include test.expect() {
.arrow-controlled-test {
inset: unset;
translate: unset;
transform-origin: center;
}
}
}
}
}
10 changes: 7 additions & 3 deletions packages/web/src/scss/tools/_dictionaries.scss
Original file line number Diff line number Diff line change
Expand Up @@ -184,17 +184,21 @@
// * $dictionary-values: map of the placements to generate
// * $offset: offset of child from its parent, typically for an arrow
// * $has-arrow: whether the component has an arrow
@mixin generate-placements($class-name, $dictionary-values, $offset: 0, $has-arrow: false) {
@mixin generate-placements($class-name, $dictionary-values, $offset: 0, $is-controlled: false, $has-arrow: false) {
$prefix: spirit-string.convert-pascal-case-to-kebab-case($class-name);

@each $placement in $dictionary-values {
.#{$class-name}[data-spirit-placement='#{$placement}'] {
@include placement.child-variant($prefix, $placement, $offset);
@include placement.child-variant($prefix, $placement, $offset, $is-controlled);
}

@if $has-arrow {
.#{$class-name}[data-spirit-placement='#{$placement}'] > .#{$class-name}__arrow {
@include placement.arrow-variant($prefix, placement.transform($placement, $main-axis-inverse: true));
@include placement.arrow-variant(
$prefix,
placement.transform($placement, $main-axis-inverse: true),
$is-controlled
);
}
}
}
Expand Down
42 changes: 23 additions & 19 deletions packages/web/src/scss/tools/_placement.scss
Original file line number Diff line number Diff line change
Expand Up @@ -207,16 +207,6 @@ $_logical-to-physical-placement-map: (
z-index: $z-index;
}

@mixin child-variant($prefix, $placement, $offset: 0) {
--#{$prefix}-offset: #{$offset}; // 1.3.a

inset: map.get($_inset-map, $placement); // 1.2
translate: map.get(-get-child-translate-map($prefix), $placement); // 1.3
transform-origin: string.unquote(
transform($placement, $main-axis-inverse: true, $cross-axis-physical: true, $join-with: ' ')
);
}

@mixin child-controlled($prefix, $offset) {
--#{$prefix}-offset-orthogonal: 0; // 2.
--#{$prefix}-offset: #{$offset}; // 2.
Expand All @@ -225,6 +215,21 @@ $_logical-to-physical-placement-map: (
translate: unset; // 2.
}

@mixin child-variant($prefix, $placement, $offset: 0, $is-controlled: false) {
--#{$prefix}-offset: #{$offset}; // 1.3.a, 2.

@if $is-controlled {
--#{$prefix}-offset-orthogonal: 0; // 2.
} @else {
inset: map.get($_inset-map, $placement); // 1.2
translate: map.get(-get-child-translate-map($prefix), $placement); // 1.3
}

transform-origin: string.unquote(
transform($placement, $main-axis-inverse: true, $cross-axis-physical: true, $join-with: ' ')
);
}

@mixin arrow($prefix, $width, $height, $corner-offset) {
--#{$prefix}-arrow-width: #{$width};
--#{$prefix}-arrow-height: #{$height};
Expand All @@ -234,14 +239,13 @@ $_logical-to-physical-placement-map: (
transform-origin: bottom center;
}

@mixin arrow-variant($prefix, $placement) {
inset: map.get($_inset-map, $placement); // 1.2
translate: map.get(-get-arrow-translate-map($prefix), $placement); // 1.3
rotate: z map.get($_arrow-rotate-map, $placement);
}
@mixin arrow-variant($prefix, $placement, $is-controlled: false) {
@if $is-controlled {
transform-origin: center; // 2.
} @else {
inset: map.get($_inset-map, $placement); // 1.2
translate: map.get(-get-arrow-translate-map($prefix), $placement); // 1.3
}

@mixin arrow-controlled() {
inset: unset; // 2.
translate: unset; // 2.
transform-origin: center; // 2.
rotate: z map.get($_arrow-rotate-map, $placement);
}

0 comments on commit 0d54664

Please sign in to comment.