Files
tabler/core/scss/tests/_generate-utility.test.scss
T

246 lines
6.1 KiB
SCSS

// Output tests for the `generate-utility` mixin from
// scss/mixins/bootstrap/_utilities.scss — the engine behind Tabler's utility
// classes. `@use "../mixins/bootstrap/utilities"` pulls in the real
// settings/variables chain, so `$enable-important-utilities` = true (hence the
// `!important` in the output). Custom properties are expected unprefixed: the
// `--tblr-` prefix is added later, by the css pipeline (.build/css-var-prefix.ts).
//
// Each `it` feeds the mixin a hand-built utility map and asserts the generated
// classes, covering the branches that shape the output: map vs list values,
// single vs multiple properties, custom class, css-var mode, pseudo-class
// states and the responsive infix.
@use 'sass:map';
@use '../mixins/bootstrap/utilities' as *;
@use '../mixins/bootstrap/breakpoints' as *;
@include describe('generate-utility') {
@include it('generates a class per key from a values map') {
@include assert() {
@include output() {
@include generate-utility(
(
property: opacity,
values: (
0: 0,
50: 0.5,
),
)
);
}
@include expect() {
.opacity-0 {
opacity: 0 !important;
}
.opacity-50 {
opacity: 0.5 !important;
}
}
}
}
@include it('zips a plain list of values into key/value pairs') {
@include assert() {
@include output() {
@include generate-utility(
(
property: float,
class: float,
values: (
left,
right,
),
)
);
}
@include expect() {
.float-left {
float: left !important;
}
.float-right {
float: right !important;
}
}
}
}
@include it('emits every property when several are given') {
@include assert() {
@include output() {
@include generate-utility(
(
property: margin-inline-end margin-inline-start,
class: mx,
values: (
0: 0,
auto: auto,
),
)
);
}
@include expect() {
.mx-0 {
margin-inline-end: 0 !important;
margin-inline-start: 0 !important;
}
.mx-auto {
margin-inline-end: auto !important;
margin-inline-start: auto !important;
}
}
}
}
@include it('sets a custom property instead of a declaration in css-var mode') {
@include assert() {
@include output() {
@include generate-utility(
(
property: opacity,
class: opacity,
css-var: true,
values: (
50: 0.5,
),
)
);
}
@include expect() {
.opacity-50 {
--opacity: 0.5;
}
}
}
}
@include it('adds a pseudo-class variant for each declared state') {
@include assert() {
@include output() {
@include generate-utility(
(
property: text-decoration,
class: text-decoration,
state: hover,
values: (underline),
)
);
}
@include expect() {
.text-decoration-underline {
text-decoration: underline !important;
}
.text-decoration-underline-hover:hover {
text-decoration: underline !important;
}
}
}
}
@include it('injects the responsive infix into the class name') {
@include assert() {
@include output() {
@include generate-utility(
(
property: display,
class: d,
values: (none),
),
'-md'
);
}
@include expect() {
.d-md-none {
display: none !important;
}
}
}
}
}
// `generate-utility` itself does not read the `responsive` key — that gate lives
// in bootstrap/utilities/_api.scss, which loops the breakpoints and only calls
// the mixin for a given infix when `map.get($utility, responsive)` is truthy (or
// it's the base, infix-less breakpoint). This reproduces that gate against a local
// breakpoint map with two utilities: one `responsive: true`, one without.
@include describe('generate-utility (responsive property)') {
$g: (
xs: 0,
sm: 576px,
md: 768px,
);
$utils: (
'display': (
property: display,
class: d,
responsive: true,
values: (
none,
flex,
),
),
'float': (
property: float,
class: float,
values: (
start: inline-start,
end: inline-end,
none: none,
),
),
);
@include it('only a `responsive: true` utility gets breakpoint-infixed variants') {
@include assert() {
@include output() {
@each $bp in map.keys($g) {
@include media-breakpoint-up($bp, $g) {
$infix: breakpoint-infix($bp, $g);
@each $key, $utility in $utils {
@if map.get($utility, responsive) or $infix == '' {
@include generate-utility($utility, $infix);
}
}
}
}
}
@include expect() {
// base breakpoint: both utilities emit bare classes
.d-none {
display: none !important;
}
.d-flex {
display: flex !important;
}
.float-start {
float: inline-start !important;
}
.float-end {
float: inline-end !important;
}
.float-none {
float: none !important;
}
// responsive breakpoints: only `d` (responsive: true) is repeated; `float` is not
@media (min-width: 576px) {
.d-sm-none {
display: none !important;
}
.d-sm-flex {
display: flex !important;
}
}
@media (min-width: 768px) {
.d-md-none {
display: none !important;
}
.d-md-flex {
display: flex !important;
}
}
}
}
}
}