Files
react-native/packages/react-native/Libraries/StyleSheet/__tests__/processBackgroundImage-test.js
T
Nishan bd0aedc8c3 feat: linear gradient android (#45433)
Summary:
- Adds `background` prop that supports CSS's linear gradient. Later this can be extended to support various other gradients and possibly CSS's background image (less motivation as better solutions exists for image)
- Extended `CSSBackgroundDrawable` to draw Linear Gradient shader while preserving the border style support.
- Style supports JS object to specify `LinearGradient`, so it can support Animated libraries.

## Changelog:
[ANDROID] [ADDED] - linear gradient

<!-- Help reviewers and the release process by writing your own changelog entry.

Pick one each for the category and type tags:

[ANDROID|GENERAL|IOS|INTERNAL] [BREAKING|ADDED|CHANGED|DEPRECATED|REMOVED|FIXED|SECURITY] - Message

For more details, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests

Pull Request resolved: https://github.com/facebook/react-native/pull/45433

Test Plan:
- Check out `processBackground-test.js` for supported syntax testcases.
- Checkout examples added in `LinearGradientExample.js`

Although the PR is tested well but open to any changes/feedback on the approach taken.

iOS PR - https://github.com/facebook/react-native/pull/45433. Separated the PRs to keep it easier to review. Both PRs can be reviewed individually.

Reviewed By: joevilches

Differential Revision: D60493360

Pulled By: NickGerleman

fbshipit-source-id: 762929c4fe16d87cbbd9ebe83ecce96a9e13192c
2024-08-01 09:38:51 -07:00

382 lines
13 KiB
JavaScript

/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* @oncall react_native
*/
'use strict';
import processBackgroundImage from '../processBackgroundImage';
const processColor = require('../processColor').default;
describe('processBackgroundImage', () => {
it('should process a simple linear gradient string', () => {
const input = 'linear-gradient(to right, red, blue)';
const result = processBackgroundImage(input);
expect(result).toEqual([
{
type: 'linearGradient',
start: {x: 0, y: 0.5},
end: {x: 1, y: 0.5},
colorStops: [
{color: processColor('red'), position: 0},
{color: processColor('blue'), position: 1},
],
},
]);
});
it('should process a diagonal linear gradient', () => {
const input = 'linear-gradient(to bottom right, red, blue)';
const result = processBackgroundImage(input);
expect(result).toEqual([
{
type: 'linearGradient',
start: {x: 0, y: 0},
end: {x: 1, y: 1},
colorStops: [
{color: processColor('red'), position: 0},
{color: processColor('blue'), position: 1},
],
},
]);
});
it('should return empty array for null values', () => {
let result = processBackgroundImage('');
expect(result).toEqual([]);
result = processBackgroundImage(null);
expect(result).toEqual([]);
result = processBackgroundImage(undefined);
expect(result).toEqual([]);
});
it('should return empty array for invalid values', () => {
let result = processBackgroundImage('linear-');
expect(result).toEqual([]);
});
it('should process a linear gradient with whitespaces in direction', () => {
const input = 'linear-gradient(to bottom right, red, blue)';
const result = processBackgroundImage(input);
expect(result).toEqual([
{
type: 'linearGradient',
start: {x: 0, y: 0},
end: {x: 1, y: 1},
colorStops: [
{color: processColor('red'), position: 0},
{color: processColor('blue'), position: 1},
],
},
]);
});
it('should process a linear gradient with random whitespaces', () => {
const input =
' linear-gradient(to bottom right, red 30%, blue 80%) ';
const result = processBackgroundImage(input);
expect(result).toEqual([
{
type: 'linearGradient',
start: {x: 0, y: 0},
end: {x: 1, y: 1},
colorStops: [
{color: processColor('red'), position: 0.3},
{color: processColor('blue'), position: 0.8},
],
},
]);
});
it('should process a linear gradient with angle', () => {
const input = 'linear-gradient(45deg, red, blue)';
const result = processBackgroundImage(input);
expect(result[0].type).toBe('linearGradient');
expect(result[0].start.x).toBeCloseTo(0.146447, 5);
expect(result[0].start.y).toBeCloseTo(0.853553, 5);
expect(result[0].end.x).toBeCloseTo(0.853553, 5);
expect(result[0].end.y).toBeCloseTo(0.146447, 5);
expect(result[0].colorStops).toEqual([
{color: processColor('red'), position: 0},
{color: processColor('blue'), position: 1},
]);
});
it('should process a linear gradient with case-insensitive angle', () => {
const input = 'linear-gradient(45Deg, red, blue)';
const result = processBackgroundImage(input);
expect(result[0].type).toBe('linearGradient');
expect(result[0].start.x).toBeCloseTo(0.146447, 5);
expect(result[0].start.y).toBeCloseTo(0.853553, 5);
expect(result[0].end.x).toBeCloseTo(0.853553, 5);
expect(result[0].end.y).toBeCloseTo(0.146447, 5);
expect(result[0].colorStops).toEqual([
{color: processColor('red'), position: 0},
{color: processColor('blue'), position: 1},
]);
});
it('linear gradient case-insensitive string', () => {
const input = 'LiNeAr-GradieNt(To Bottom, Red, Blue)';
const result = processBackgroundImage(input);
expect(result[0].type).toBe('linearGradient');
expect(result[0].start).toEqual({x: 0.5, y: 0});
expect(result[0].end).toEqual({x: 0.5, y: 1});
expect(result[0].colorStops).toEqual([
{color: processColor('red'), position: 0},
{color: processColor('blue'), position: 1},
]);
});
it('should process a linear gradient with case-insensitive direction enum', () => {
const input = 'linear-gradient(tO Right, red, blue)';
const result = processBackgroundImage(input);
expect(result[0].start).toEqual({x: 0, y: 0.5});
expect(result[0].end).toEqual({x: 1, y: 0.5});
expect(result[0].colorStops).toEqual([
{color: processColor('red'), position: 0},
{color: processColor('blue'), position: 1},
]);
});
it('should process a linear gradient with case-insensitive colors', () => {
const input =
'linear-gradient(to right, Rgba(0, 0, 0, 0.5), Blue, Hsla(0, 100%, 50%, 0.5))';
const result = processBackgroundImage(input);
expect(result[0].start).toEqual({x: 0, y: 0.5});
expect(result[0].end).toEqual({x: 1, y: 0.5});
expect(result[0].colorStops).toEqual([
{color: processColor('rgba(0, 0, 0, 0.5)'), position: 0},
{color: processColor('blue'), position: 0.5},
{color: processColor('hsla(0, 100%, 50%, 0.5)'), position: 1},
]);
});
it('should process multiple linear gradients', () => {
const input = `
linear-gradient(to right, red, blue),
linear-gradient(to bottom, green, yellow)`;
const result = processBackgroundImage(input);
expect(result).toHaveLength(2);
expect(result[0].type).toEqual('linearGradient');
expect(result[0].start).toEqual({x: 0, y: 0.5});
expect(result[0].end).toEqual({x: 1, y: 0.5});
expect(result[0].colorStops).toEqual([
{color: processColor('red'), position: 0},
{color: processColor('blue'), position: 1},
]);
expect(result[1].type).toEqual('linearGradient');
expect(result[1].start).toEqual({x: 0.5, y: 0});
expect(result[1].end).toEqual({x: 0.5, y: 1});
expect(result[1].colorStops).toEqual([
{color: processColor('green'), position: 0},
{color: processColor('yellow'), position: 1},
]);
});
it('should process a linear gradient with multiple color stops', () => {
const input = 'linear-gradient(to right, red 0%, green 50%, blue 100%)';
const result = processBackgroundImage(input);
expect(result[0].colorStops).toEqual([
{color: processColor('red'), position: 0},
{color: processColor('green'), position: 0.5},
{color: processColor('blue'), position: 1},
]);
});
it('should add color stop postion if position is not specified', () => {
const input =
'linear-gradient(to right, red, green, blue 60%, yellow, purple)';
const result = processBackgroundImage(input);
expect(result[0].colorStops).toEqual([
{color: processColor('red'), position: 0},
{color: processColor('green'), position: 0.25},
{color: processColor('blue'), position: 0.6},
{color: processColor('yellow'), position: 0.75},
{color: processColor('purple'), position: 1},
]);
});
it('should process a linear gradient with rgba colors', () => {
const input =
'linear-gradient(to right, rgba(255,0,0,0.5), rgba(0,0,255,0.8))';
const result = processBackgroundImage(input);
expect(result[0].colorStops).toEqual([
{color: processColor('rgba(255,0,0,0.5)'), position: 0},
{color: processColor('rgba(0,0,255,0.8)'), position: 1},
]);
});
it('should process a linear gradient with hsl colors', () => {
const input = `linear-gradient(hsl(330, 100%, 45.1%), hsl(0, 100%, 50%))`;
const result = processBackgroundImage(input);
expect(result[0].colorStops).toEqual([
{color: processColor('hsl(330, 100%, 45.1%)'), position: 0},
{color: processColor('hsl(0, 100%, 50%)'), position: 1},
]);
});
it('should process a linear gradient without direction', () => {
const input = 'linear-gradient(#e66465, #9198e5)';
const result = processBackgroundImage(input);
expect(result[0].colorStops).toEqual([
{color: processColor('#e66465'), position: 0},
{color: processColor('#9198e5'), position: 1},
]);
});
it('should process multiple gradients with spaces', () => {
const input = `linear-gradient(to right ,
rgba(255,0,0,0.5), rgba(0,0,255,0.8)),
linear-gradient(to bottom , rgba(255,0,0,0.9) , rgba(0,0,255,0.2) )`;
const result = processBackgroundImage(input);
expect(result).toHaveLength(2);
expect(result[0].start).toEqual({x: 0, y: 0.5});
expect(result[0].end).toEqual({x: 1, y: 0.5});
expect(result[1].start).toEqual({x: 0.5, y: 0});
expect(result[1].end).toEqual({x: 0.5, y: 1});
expect(result[0].colorStops).toEqual([
{color: processColor('rgba(255,0,0,0.5)'), position: 0},
{color: processColor('rgba(0,0,255,0.8)'), position: 1},
]);
expect(result[1].colorStops).toEqual([
{color: processColor('rgba(255,0,0,0.9)'), position: 0},
{color: processColor('rgba(0,0,255,0.2)'), position: 1},
]);
});
it('should return empty array for invalid color in linear gradient', () => {
const input = 'linear-gradient(45deg, rede, blue)';
const result = processBackgroundImage(input);
expect(result).toEqual([]);
});
it('should return empty array for invalid angle in linear gradient', () => {
const input = 'linear-gradient(45 deg, red, blue)';
const result = processBackgroundImage(input);
expect(result).toEqual([]);
});
it('should return empty array for invalid direction enum in linear gradient', () => {
const input = 'linear-gradient(to left2, red, blue)';
const result = processBackgroundImage(input);
expect(result).toEqual([]);
});
it('should return empty array for invalid color stop unit', () => {
const input = 'linear-gradient(to left, red 5, blue)';
const result = processBackgroundImage(input);
expect(result).toEqual([]);
});
it('should process an array of style objects', () => {
const input = [
{
type: 'linearGradient',
direction: 'to bottom right',
colorStops: [
{color: 'red', position: '0%'},
{color: 'blue', position: '100%'},
],
},
];
const result = processBackgroundImage(input);
expect(result).toEqual([
{
type: 'linearGradient',
start: {x: 0, y: 0},
end: {x: 1, y: 1},
colorStops: [
{color: processColor('red'), position: 0},
{color: processColor('blue'), position: 1},
],
},
]);
});
it('should process an style object with default direction', () => {
const input = [
{
type: 'linearGradient',
colorStops: [{color: 'red'}, {color: 'blue'}],
},
];
const result = processBackgroundImage(input);
expect(result[0].start).toEqual({x: 0.5, y: 0});
expect(result[0].end).toEqual({x: 0.5, y: 1});
});
it('should process style object with direction enum', () => {
const input = [
{
type: 'linearGradient',
direction: 'to right',
colorStops: [{color: 'red'}, {color: 'blue'}],
},
];
const result = processBackgroundImage(input);
expect(result[0].start).toEqual({x: 0, y: 0.5});
expect(result[0].end).toEqual({x: 1, y: 0.5});
});
it('should process style object with direction angle', () => {
const input = [
{
type: 'linearGradient',
direction: '45deg',
colorStops: [{color: 'red'}, {color: 'blue'}],
},
];
const result = processBackgroundImage(input);
expect(result[0].start.x).toBeCloseTo(0.146447, 5);
expect(result[0].start.y).toBeCloseTo(0.853553, 5);
expect(result[0].end.x).toBeCloseTo(0.853553, 5);
expect(result[0].end.y).toBeCloseTo(0.146447, 5);
});
it('should process an style object with mix of default and undefined stop positions', () => {
const input = [
{
type: 'linearGradient',
colorStops: [
{color: 'red'},
{color: 'blue'},
{color: 'green'},
{color: 'purple', position: '80%'},
{color: 'pink'},
],
},
];
const result = processBackgroundImage(input);
expect(result[0].colorStops).toEqual([
{
color: processColor('red'),
position: 0,
},
{
color: processColor('blue'),
position: 0.25,
},
{
color: processColor('green'),
position: 0.5,
},
{
color: processColor('purple'),
position: 0.8,
},
{
color: processColor('pink'),
position: 1,
},
]);
});
});