diff options
12 files changed, 1403 insertions, 59 deletions
diff --git a/server/src/main/java/com/vaadin/ui/components/colorpicker/ColorPickerPreview.java b/server/src/main/java/com/vaadin/ui/components/colorpicker/ColorPickerPreview.java index f68ae0ce9f..57da8949bd 100644 --- a/server/src/main/java/com/vaadin/ui/components/colorpicker/ColorPickerPreview.java +++ b/server/src/main/java/com/vaadin/ui/components/colorpicker/ColorPickerPreview.java @@ -16,9 +16,15 @@ package com.vaadin.ui.components.colorpicker; import java.util.Objects; +import java.util.logging.Level; +import java.util.logging.Logger; import com.vaadin.data.HasValue; +import com.vaadin.server.AbstractErrorMessage.ContentMode; +import com.vaadin.server.ErrorMessage; +import com.vaadin.server.UserError; import com.vaadin.shared.Registration; +import com.vaadin.shared.ui.ErrorLevel; import com.vaadin.shared.ui.colorpicker.Color; import com.vaadin.ui.Component; import com.vaadin.ui.CssLayout; @@ -31,6 +37,8 @@ import com.vaadin.ui.TextField; * @since 7.0.0 */ public class ColorPickerPreview extends CssLayout implements HasValue<Color> { + private static final Logger LOGGER = Logger + .getLogger(ColorPickerPreview.class.getName()); private static final String STYLE_DARK_COLOR = "v-textfield-dark"; private static final String STYLE_LIGHT_COLOR = "v-textfield-light"; @@ -86,6 +94,7 @@ public class ColorPickerPreview extends CssLayout implements HasValue<Color> { String colorCSS = color.getCSS(); field.setValue(colorCSS); + field.setComponentError(null); oldValue = colorCSS; @@ -120,68 +129,29 @@ public class ColorPickerPreview extends CssLayout implements HasValue<Color> { } private void valueChange(ValueChangeEvent<String> event) { + ErrorMessage errorMessage = null; String value = event.getValue(); + value = Objects.toString(value, "").trim(); Color oldColor = color; try { - if (value != null) { - /* - * Description of supported formats see - * http://www.w3schools.com/cssref/css_colors_legal.asp - */ - if (value.length() == 7 && value.startsWith("#")) { - // CSS color format (e.g. #000000) - int red = Integer.parseInt(value.substring(1, 3), 16); - int green = Integer.parseInt(value.substring(3, 5), 16); - int blue = Integer.parseInt(value.substring(5, 7), 16); - color = new Color(red, green, blue); - - } else if (value.startsWith("rgb")) { - // RGB color format rgb/rgba(255,255,255,0.1) - String[] colors = value.substring(value.indexOf('(') + 1, - value.length() - 1).split(","); - - int red = Integer.parseInt(colors[0]); - int green = Integer.parseInt(colors[1]); - int blue = Integer.parseInt(colors[2]); - if (colors.length > 3) { - int alpha = (int) (Double.parseDouble(colors[3]) - * 255d); - color = new Color(red, green, blue, alpha); - } else { - color = new Color(red, green, blue); - } - - } else if (value.startsWith("hsl")) { - // HSL color format hsl/hsla(100,50%,50%,1.0) - String[] colors = value.substring(value.indexOf('(') + 1, - value.length() - 1).split(","); - - int hue = Integer.parseInt(colors[0]); - int saturation = Integer - .parseInt(colors[1].replace("%", "")); - int lightness = Integer - .parseInt(colors[2].replace("%", "")); - int rgb = Color.HSLtoRGB(hue, saturation, lightness); - - if (colors.length > 3) { - int alpha = (int) (Double.parseDouble(colors[3]) - * 255d); - color = new Color(rgb); - color.setAlpha(alpha); - } else { - color = new Color(rgb); - } - } - - oldValue = value; - fireEvent(new ValueChangeEvent<>(this, oldColor, - event.isUserOriginated())); - } - } catch (NumberFormatException nfe) { - // Revert value - field.setValue(oldValue); + /* + * Description of supported formats see + * http://www.w3schools.com/cssref/css_colors_legal.asp + */ + color = ColorUtil.stringToColor(value); + + oldValue = value; + fireEvent(new ValueChangeEvent<>(this, oldColor, + event.isUserOriginated())); + } catch (NumberFormatException e) { + // Pattern matching ensures the validity of + // the input, this should never happen + LOGGER.log(Level.INFO, e.getMessage()); + errorMessage = new UserError(getUserErrorText(value), + ContentMode.TEXT, ErrorLevel.WARNING); } + field.setComponentError(errorMessage); } @Override @@ -224,4 +194,18 @@ public class ColorPickerPreview extends CssLayout implements HasValue<Color> { } } } -} + + /** + * Get the client error message text for color input parsing error. + * + * @param value + * input which caused the error + * @return error message text + */ + protected String getUserErrorText(String value) { + return value.isEmpty() ? "Input cannot be empty" + : "Input '".concat(value) + .concat("' is not in any recognized format"); + } + +}
\ No newline at end of file diff --git a/server/src/main/java/com/vaadin/ui/components/colorpicker/ColorUtil.java b/server/src/main/java/com/vaadin/ui/components/colorpicker/ColorUtil.java new file mode 100644 index 0000000000..ae942ea0ce --- /dev/null +++ b/server/src/main/java/com/vaadin/ui/components/colorpicker/ColorUtil.java @@ -0,0 +1,229 @@ +/* + * Copyright 2000-2018 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.ui.components.colorpicker; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.vaadin.shared.ui.colorpicker.Color; + +/** + * Utility class for matching and parsing {@link Color} objects from + * {@code String} input. + * + * Description of supported formats see + * http://www.w3schools.com/cssref/css_colors_legal.asp + * + * @since + */ +public class ColorUtil { + private ColorUtil() { + } + + /** + * Parses {@link Color} from any of the following {@link String} inputs: + * <br> + * - RGB hex (e.g. "#FFAA00"), {@link #HEX_PATTERN}<br> + * - RGB "function" (e.g. "rgb(128,0,255)"), {@link #RGB_PATTERN}<br> + * - RGBA "function" (e.g. "rgba(50,50,50,0.2)"), {@link #RGBA_PATTERN}<br> + * - HSL "function" (e.g. "hsl(50,50,50)"), {@link #HSL_PATTERN}<br> + * - HSLA "function" (e.g. "hsl(50,50,50,0.2)"), {@link #HSLA_PATTERN} + * <p> + * Parsing is case-insensitive. + * + * @param input + * String input + * @return {@link Color} parsed from input + * @throws NumberFormatException + * Input does not match any recognized pattern + */ + public static Color stringToColor(String input) { + Matcher m = HEX_PATTERN.matcher(input); + if (m.matches()) { + return getHexPatternColor(m); + } + m = RGB_PATTERN.matcher(input); + if (m.matches()) { + return getRGBPatternColor(m); + } + m = RGBA_PATTERN.matcher(input); + if (m.matches()) { + return getRGBAPatternColor(m); + } + m = HSL_PATTERN.matcher(input); + if (m.matches()) { + return getHSLPatternColor(m); + } + m = HSLA_PATTERN.matcher(input); + if (m.matches()) { + return getHSLAPatternColor(m); + } + + throw new NumberFormatException("Parsing color from input failed."); + } + + /** + * Parses {@link Color} from matched hexadecimal {@link Matcher}. + * + * @param matcher + * {@link Matcher} matching hexadecimal pattern with named regex + * groups {@code red}, {@code green}, and {@code blue} + * @return {@link Color} parsed from {@link Matcher} + */ + public static Color getHexPatternColor(Matcher matcher) { + int red = Integer.parseInt(matcher.group("red"), 16); + int green = Integer.parseInt(matcher.group("green"), 16); + int blue = Integer.parseInt(matcher.group("blue"), 16); + return new Color(red, green, blue); + } + + /** + * Parses {@link Color} from matched RGB {@link Matcher}. + * + * @param matcher + * {@link Matcher} matching RGB pattern with named regex groups + * {@code red}, {@code green}, and {@code blue} + * @return {@link Color} parsed from {@link Matcher} + */ + public static Color getRGBPatternColor(Matcher matcher) { + int red = Integer.parseInt(matcher.group("red")); + int green = Integer.parseInt(matcher.group("green")); + int blue = Integer.parseInt(matcher.group("blue")); + return new Color(red, green, blue); + } + + /** + * Parses {@link Color} from matched RGBA {@link Matcher}. + * + * @param matcher + * {@link Matcher} matching RGBA pattern with named regex groups + * {@code red}, {@code green}, {@code blue}, and {@code alpha} + * @return {@link Color} parsed from {@link Matcher} + */ + public static Color getRGBAPatternColor(Matcher matcher) { + Color c = getRGBPatternColor(matcher); + c.setAlpha((int) (Double.parseDouble(matcher.group("alpha")) * 255d)); + return c; + } + + /** + * Parses {@link Color} from matched HSL {@link Matcher}. + * + * @param matcher + * {@link Matcher} matching HSL pattern with named regex groups + * {@code hue}, {@code saturation}, and {@code light} + * @return {@link Color} parsed from {@link Matcher} + */ + public static Color getHSLPatternColor(Matcher matcher) { + int hue = Integer.parseInt(matcher.group("hue")); + int saturation = Integer.parseInt(matcher.group("saturation")); + int light = Integer.parseInt(matcher.group("light")); + int rgb = Color.HSLtoRGB(hue, saturation, light); + return new Color(rgb); + } + + /** + * Parses {@link Color} from matched HSLA {@link Matcher}. + * + * @param matcher + * {@link Matcher} matching HSLA pattern with named regex groups + * {@code hue}, {@code saturation}, {@code light}, and + * {@code alpha} + * @return {@link Color} parsed from {@link Matcher} + */ + public static Color getHSLAPatternColor(Matcher matcher) { + Color c = getHSLPatternColor(matcher); + c.setAlpha((int) (Double.parseDouble(matcher.group("alpha")) * 255d)); + return c; + } + + /** + * Case-insensitive {@link Pattern} with regular expression matching the + * default hexadecimal color presentation pattern:<br> + * '#' followed by six <code>[\da-fA-F]</code> characters. + * <p> + * Pattern contains named groups <code>red</code>, <code>green</code>, and + * <code>blue</code>, which represent the individual values. + */ + public static final Pattern HEX_PATTERN = Pattern.compile( + "(?i)^#\\s*(?<red>[\\da-f]{2})(?<green>[\\da-f]{2})(?<blue>[\\da-f]{2}" + + ")\\s*$"); + /** + * Case-insensitive {@link Pattern} with regular expression matching common + * RGB color presentation patterns:<br> + * 'rgb' followed by three [0-255] number values. Values can be separated + * with either comma or whitespace. + * <p> + * Pattern contains named groups <code>red</code>, <code>green</code>, and + * <code>blue</code>, which represent the individual values. + */ + public static final Pattern RGB_PATTERN = Pattern.compile( + "(?i)^rgb\\(\\s*(?<red>[01]?\\d{1,2}|2[0-4]\\d|25[0-5])(?:\\s*[,+|\\" + + "s+]\\s*)(?<green>[01]?\\d\\d?|2[0-4]\\d|25[0-5])(?:\\s*[," + + "+|\\s+]\\s*)(?<blue>[01]?\\d\\d?|2[0-4]\\d|25[0-5])\\s*\\" + + ")$"); + /** + * Case-insensitive {@link Pattern} with regular expression matching common + * RGBA presentation patterns:<br> + * 'rgba' followed by three [0-255] values and one [0.0-1.0] value. Values + * can be separated with either comma or whitespace. The only accepted + * decimal marker is point ('.'). + * <p> + * Pattern contains named groups <code>red</code>, <code>green</code>, + * <code>blue</code>, and <code>alpha</code>, which represent the individual + * values. + */ + public static final Pattern RGBA_PATTERN = Pattern.compile( + "(?i)^rgba\\(\\s*(?<red>[01]?\\d{1,2}|2[0-4]\\d|25[0-5])(?:\\s*[,+|" + + "\\s+]\\s*)(?<green>[01]?\\d\\d?|2[0-4]\\d|25[0-5])(?:\\s" + + "*[,+|\\s+]\\s*)(?<blue>[01]?\\d\\d?|2[0-4]\\d|25[0-5])(?" + + ":\\s*[,+|\\s+]\\s*)(?<alpha>0(?:\\.\\d{1,2})?|0?(?:\\.\\" + + "d{1,2})|1(?:\\.0{1,2})?)\\s*\\)$"); + + /** + * Case-insensitive {@link Pattern} with regular expression matching common + * HSL presentation patterns:<br> + * 'hsl' followed by one [0-360] value and two [0-100] percentage value. + * Values can be separated with either comma or whitespace. The percent sign + * ('%') is optional. + * <p> + * Pattern contains named groups <code>hue</code>,<code>saturation</code>, + * and <code>light</code>, which represent the individual values. + */ + public static final Pattern HSL_PATTERN = Pattern.compile( + "(?i)hsl\\(\\s*(?<hue>[12]?\\d{1,2}|3[0-5]\\d|360)(?:\\s*[,+|\\s+]" + + "\\s*)(?<saturation>\\d{1,2}|100)(?:\\s*%?\\s*[,+|\\s+]\\" + + "s*)(?<light>\\d{1,2}|100)(?:\\s*%?\\s*)\\)$"); + + /** + * Case-insensitive {@link Pattern} with regular expression matching common + * HSLA presentation patterns:<br> + * 'hsla' followed by one [0-360] value, two [0-100] percentage values, and + * one [0.0-1.0] value. Values can be separated with either comma or + * whitespace. The percent sign ('%') is optional. The only accepted decimal + * marker is point ('.'). + * <p> + * Pattern contains named groups <code>hue</code>,<code>saturation</code>, + * <code>light</code>, and <code>alpha</code>, which represent the + * individual values. + */ + public static final Pattern HSLA_PATTERN = Pattern.compile( + "(?i)hsla\\(\\s*(?<hue>[12]?\\d{1,2}|3[0-5]\\d|360)(?:\\s*[,+|\\s+" + + "]\\s*)(?<saturation>\\d{1,2}|100)(?:\\s*%?\\s*[,+|\\s+]\\s*" + + ")(?<light>\\d{1,2}|100)(?:\\s*%?[,+|\\s+]\\s*)(?<alpha>" + + "0(?:\\.\\d{1,2})?|0?(?:\\.\\d{1,2})|1(?:\\.0{1,2})?)" + + "\\s*\\)$"); +} diff --git a/server/src/test/java/com/vaadin/tests/server/ClassesSerializableTest.java b/server/src/test/java/com/vaadin/tests/server/ClassesSerializableTest.java index 0b6d67f7a7..0827fc262c 100644 --- a/server/src/test/java/com/vaadin/tests/server/ClassesSerializableTest.java +++ b/server/src/test/java/com/vaadin/tests/server/ClassesSerializableTest.java @@ -94,6 +94,7 @@ public class ClassesSerializableTest { "com\\.vaadin\\.server\\.JsonCodec\\$1", // "com\\.vaadin\\.server\\.communication\\.PushConnection", // "com\\.vaadin\\.server\\.communication\\.AtmospherePushConnection.*", // + "com\\.vaadin\\.ui\\.components\\.colorpicker\\.ColorUtil", // "com\\.vaadin\\.util\\.ConnectorHelper", // "com\\.vaadin\\.server\\.VaadinSession\\$FutureAccess", // "com\\.vaadin\\.external\\..*", // diff --git a/server/src/test/java/com/vaadin/tests/server/component/colorpicker/HSLAPatternParsingTest.java b/server/src/test/java/com/vaadin/tests/server/component/colorpicker/HSLAPatternParsingTest.java new file mode 100644 index 0000000000..548e61df26 --- /dev/null +++ b/server/src/test/java/com/vaadin/tests/server/component/colorpicker/HSLAPatternParsingTest.java @@ -0,0 +1,88 @@ +package com.vaadin.tests.server.component.colorpicker; + +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.regex.Matcher; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +import com.vaadin.shared.ui.colorpicker.Color; +import com.vaadin.ui.components.colorpicker.ColorUtil; + +@RunWith(value = Parameterized.class) +public class HSLAPatternParsingTest { + + @Parameter(value = 0) + public String input; + + @Parameter(value = 1) + public int expectedHue; + @Parameter(value = 2) + public int expectedSaturation; + @Parameter(value = 3) + public int expectedLight; + @Parameter(value = 4) + public int expectedAlpha; + + @Parameter(value = 5) + public boolean expectedMatches; + + @Parameters(name = "{index}: testHSLAData({0}) = ({1},{2},{3},{4},{5})") + public static Collection<Object[]> hsladata() { + Object[][] validValues = { { "hsla(0,0,0,0)", 0, 0, 0, 0, true }, + { "HSLA(0, 0, 0, 0)", 0, 0, 0, 0, true }, + { "hsla(0,0%,0%, 0.1)", 0, 0, 0, 25, true }, + { "hsla(0 0 0 0.00 )", 0, 0, 0, 0, true }, + { "hsla(0 0% 0% 0.50)", 0, 0, 0, 127, true }, + { "hsla(360,100,100,1.0)", 360, 100, 100, 255, true }, + { "hsla(360, 100, 100, 1.0)", 360, 100, 100, 255, true }, + { "hsla(360, 100%, 100%, 1.00)", 360, 100, 100, 255, true }, + { "hsla(360 100% 100% 1.00)", 360, 100, 100, 255, true }, + { "hsla(20, 10, 10, 0.24)", 20, 10, 10, 61, true }, + { "hsla(100, 0, 50, 0.8)", 100, 0, 50, 204, true }, + { "hsla(269, 50, 0, .6)", 269, 50, 0, 153, true }, }; + Object[][] invalidValues = { { "hsla(361,0,0,0)", 0, 0, 0, 0, false }, + { "hsla(0.0, 0, 0, 0)", 0, 0, 0, 0, false }, + { "hsla(0,0%,0%, 1.1)", 0, 0, 0, 0, false }, + { "hsla(0 0 0 0.009 )", 0, 0, 0, 0, false }, + { "hsla(0 0% -100% 0.50)", 0, 0, 0, 0, false }, + { "hsla(360,1000,100,1.0)", 0, 0, 0, 0, false }, + { "hsla(0, 100, 100, 2.0)", 0, 0, 0, 0, false }, + { "hsla(360, 100%, 100%, 10.00)", 0, 0, 0, 0, false }, + { "hsl a(360 100% 100% 1.)", 0, 0, 0, 0, false }, + { "hsla(20, -10, 10, 0.24)", 0, 0, 0, 0, false }, + { "hsla(400, 0, 50, 0.8)", 0, 0, 0, 0, false }, + { "hsla(200, 50, 0, 0.996)", 0, 0, 0, 0, false }, + { "hsla 200, 50, 0, 0.9", 0, 0, 0, 0, false }, + { "hsla(0,0,0,0.)", 0, 0, 0, 0, false } }; + + ArrayList<Object[]> values = new ArrayList<>(); + Collections.addAll(values, validValues); + Collections.addAll(values, invalidValues); + + return values; + } + + @Test + public void testHSLAData() { + Matcher m = ColorUtil.HSLA_PATTERN.matcher(input); + boolean matches = m.matches(); + if (expectedMatches) { + Color expectedColor = new Color(Color.HSLtoRGB(expectedHue, + expectedSaturation, expectedLight)); + expectedColor.setAlpha(expectedAlpha); + Color c1 = ColorUtil.getHSLAPatternColor(m); + assertTrue(expectedColor.equals(c1)); + } else { + assertTrue(!matches); + } + } + +} diff --git a/server/src/test/java/com/vaadin/tests/server/component/colorpicker/HSLPatternParsingTest.java b/server/src/test/java/com/vaadin/tests/server/component/colorpicker/HSLPatternParsingTest.java new file mode 100644 index 0000000000..738388a70d --- /dev/null +++ b/server/src/test/java/com/vaadin/tests/server/component/colorpicker/HSLPatternParsingTest.java @@ -0,0 +1,89 @@ +package com.vaadin.tests.server.component.colorpicker; + +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.regex.Matcher; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +import com.vaadin.shared.ui.colorpicker.Color; +import com.vaadin.ui.components.colorpicker.ColorUtil; + +@RunWith(value = Parameterized.class) +public class HSLPatternParsingTest { + + @Parameter(value = 0) + public String input; + + @Parameter(value = 1) + public Color expectedColor; + + @Parameter(value = 2) + public boolean expectedMatches; + + @Parameters(name = "{index}: testHSLData({0}) = ({1},{2})") + public static Collection<Object[]> hsldata() { + Object[][] validValues = { + { "hsl(0,0,0)", new Color(Color.HSLtoRGB(0, 0, 0)), true }, + { "hsl(0, 0, 0)", new Color(Color.HSLtoRGB(0, 0, 0)), true }, + { "hsl(0,0%,0% )", new Color(Color.HSLtoRGB(0, 0, 0)), true }, + { "hsl(0 0 0)", new Color(Color.HSLtoRGB(0, 0, 0)), true }, + { "hsl(0 0% 0%)", new Color(Color.HSLtoRGB(0, 0, 0)), true }, + { "hsl(360,100,100)", new Color(Color.HSLtoRGB(360, 100, 100)), + true }, + { "hsl(360, 100, 100)", + new Color(Color.HSLtoRGB(360, 100, 100)), true }, + { "hsl(360, 100%, 100%)", + new Color(Color.HSLtoRGB(360, 100, 100)), true }, + { "hsl(360 100% 100%)", + new Color(Color.HSLtoRGB(360, 100, 100)), true }, + { "hsl(20, 10, 10)", new Color(Color.HSLtoRGB(20, 10, 10)), + true }, + { "hsl(100, 0, 50)", new Color(Color.HSLtoRGB(100, 0, 50)), + true }, + { "hsl(200, 50, 0)", new Color(Color.HSLtoRGB(200, 50, 0)), + true }, + { "hsl(200, 50, 05)", new Color(Color.HSLtoRGB(200, 50, 5)), + true } }; + Object[][] invalidValues = { { "hsl(361,0,0)", null, false }, + { "hsl(-0, 0, 0)", null, false }, + { "hsl (100%,0%,0% )", null, false }, + { "hsl(0 101 0)", null, false }, + { "hsl(0 0% -99%)", null, false }, + { "hsl(360,100,10 0)", null, false }, + { "hsl(360, 100, 101)", null, false }, + { "hsl(360, 110%, 100%)", null, false }, + { "hsl(3600 100% 100%)", null, false }, + { "hs l(420, 10, 10)", null, false }, + { "hsl(100, 0, 5,0)", null, false }, + { "hsla(200, 50, 0)", null, false }, + { "hsl(0,0,0", null, false }, { "rgb\\(\\.*", null, false }, + { "hsl(\\.*)", null, false }, { "#\\d.*", null, false }, + { "", null, false } }; + ArrayList<Object[]> values = new ArrayList<>(); + Collections.addAll(values, validValues); + Collections.addAll(values, invalidValues); + + return values; + } + + @Test + public void testHSLData() { + Matcher m = ColorUtil.HSL_PATTERN.matcher(input); + boolean matches = m.matches(); + if (expectedMatches) { + Color c1 = ColorUtil.getHSLPatternColor(m); + assertTrue(expectedColor.equals(c1)); + } else { + assertTrue(!matches); + } + } + +} diff --git a/server/src/test/java/com/vaadin/tests/server/component/colorpicker/HexPatternParsingTest.java b/server/src/test/java/com/vaadin/tests/server/component/colorpicker/HexPatternParsingTest.java new file mode 100644 index 0000000000..a79e928ddf --- /dev/null +++ b/server/src/test/java/com/vaadin/tests/server/component/colorpicker/HexPatternParsingTest.java @@ -0,0 +1,76 @@ +package com.vaadin.tests.server.component.colorpicker; + +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.regex.Matcher; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +import com.vaadin.shared.ui.colorpicker.Color; +import com.vaadin.ui.components.colorpicker.ColorUtil; + +@RunWith(value = Parameterized.class) +public class HexPatternParsingTest { + + @Parameter(value = 0) + public String input; + + @Parameter(value = 1) + public int expectedRed; + @Parameter(value = 2) + public int expectedGreen; + @Parameter(value = 3) + public int expectedBlue; + + @Parameter(value = 4) + public boolean expectedMatches; + + @Parameters(name = "{index}: textValidHEX({0}) = ({1},{2},{3},{4})") + public static Collection<Object[]> hexdata() { + Object[][] validValues = { { "#000000", 0, 0, 0, true }, + { "#ffffff", 255, 255, 255, true }, + { "#FF00ff", 255, 0, 255, true }, { "#aa90e3",170,144,227, true }, + { "#016953", 1, 105, 83, true }, + { "#bC64D0", 188, 100, 208, true }, + { "#F100FF", 241, 0, 255, true }, + { "#F0E9a5", 240, 233, 165, true }, + { "#990077", 153, 0, 119, true } }; + Object[][] invalidValues = { { "#0000000", 0, 0, 0, false }, + { "#ffgfff", 0, 0, 0, false }, { "#FF10f", 0, 0, 0, false }, + { "#aa9", 0, 0, 0, false }, { "#03", 0, 0, 0, false }, + { "#aab3c4c", 0, 0, 0, false }, { "#6010", 0, 0, 0, false }, + { "#CCCC", 0, 0, 0, false }, { "#9", 0, 0, 0, false }, + { "#10 10 10", 0, 0, 0, false }, { "101010", 0, 0, 0, false }, + { "#10101q", 0, 0, 0, false }, + { "\\s%\\d[0-9]", 0, 0, 0, false }, + { "#\\d.*", 0, 0, 0, false }, { "rgb\\(\\.*", 0, 0, 0, false }, + { "#\\d\\d\\d", 0, 0, 0, false }, { "#\\d.*", 0, 0, 0, false }, + { "", 0, 0, 0, false }, { "hsl(25,25,25)", 0, 0, 0, false } }; + ArrayList<Object[]> values = new ArrayList<>(); + Collections.addAll(values, validValues); + Collections.addAll(values, invalidValues); + + return values; + } + + @Test + public void testValidHEX() { + Matcher m = ColorUtil.HEX_PATTERN.matcher(input); + boolean matches = m.matches(); + if (expectedMatches) { + Color c = new Color(expectedRed, expectedGreen, expectedBlue); + Color c1 = ColorUtil.getHexPatternColor(m); + assertTrue(c.equals(c1)); + } else { + assertTrue(!matches); + } + } + +} diff --git a/server/src/test/java/com/vaadin/tests/server/component/colorpicker/RGBAPatternParsingTest.java b/server/src/test/java/com/vaadin/tests/server/component/colorpicker/RGBAPatternParsingTest.java new file mode 100644 index 0000000000..71ea7c7d7b --- /dev/null +++ b/server/src/test/java/com/vaadin/tests/server/component/colorpicker/RGBAPatternParsingTest.java @@ -0,0 +1,92 @@ +package com.vaadin.tests.server.component.colorpicker; + +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.regex.Matcher; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +import com.vaadin.shared.ui.colorpicker.Color; +import com.vaadin.ui.components.colorpicker.ColorUtil; + +@RunWith(value = Parameterized.class) +public class RGBAPatternParsingTest { + + @Parameter(value = 0) + public String input; + + @Parameter(value = 1) + public int expectedRed; + @Parameter(value = 2) + public int expectedGreen; + @Parameter(value = 3) + public int expectedBlue; + @Parameter(value = 4) + public int expectedAlpha; + + @Parameter(value = 5) + public boolean expectedMatches; + + @Parameters(name = "{index}: testRGBAData({0}) = ({1},{2},{3},{4},{5})") + public static Collection<Object[]> rgbdata() { + Object[][] validValues = { { "rgba(0,0,0,0)", 0, 0, 0, 0, true }, + { "RGBA(0, 0, 0, 0 )", 0, 0, 0, 0, true }, + { "rgba(0 0 0 0.00)", 0, 0, 0, 0, true }, + { "rgba(1 1 1 1.00)", 1, 1, 1, 255, true }, + { "rgba(0 100 200 0.50)", 0, 100, 200, 127, true }, + { "rgba(255,255,255,1.0)", 255, 255, 255, 255, true }, + { "rgba(255, 255, 255, 1.0)", 255, 255, 255, 255, true }, + { "rgba(255 255 255 0)", 255, 255, 255, 0, true }, + { "rgba(1, 10, 100, 0.00)", 1, 10, 100, 0, true } }; + Object[][] invalidValues = { { "rgba(256,0,0,0)", 0, 0, 0, 0, false }, + { "rgba(0, 256, 0, -0 )", 0, 0, 0, 0, false }, + { "rgba(0,0,10.0, 00)", 0, 0, 0, 0, false }, + { "rgba(0 0 0 2.00)", 0, 0, 0, 0, false }, + { "rgba(0 -99 0 0.50)", 0, 0, 0, 0, false }, + { "rgba(0,255%,255,1.0)", 0, 0, 0, 0, false }, + { "rgba(255, 255, 255, 1.05)", 0, 0, 0, 0, false }, + { "rgba(255, 255, 255, 1.50)", 0, 0, 0, 0, false }, + { "rgb a(255 255 0.005)", 0, 0, 0, 0, false }, + { "rgba(163, 256, 1000, 0.24)", 0, 0, 0, 0, false }, + { "rgba(100, 0.5, 250, 0.8)", 0, 0, 0, 0, false }, + { "rgba(, 50, 0, 0.6)", 0, 0, 0, 0, false }, + { "rgba(200, 50, 0, 10.6)", 0, 0, 0, 0, false }, + { "rgba 200, 50, 0, 1.", 0, 0, 0, 0, false }, + { "rgba(0,0,0,0.)", 0, 0, 0, 0, false }, + { "rgb(200, 50, 0)", 0, 0, 0, 0, false }, + { "hsla,0(10,0,0)", 0, 0, 0, 0, false }, + { "rgba(\\s.*\\d[0-9])", 0, 0, 0, 0, false }, + { "rgba(\\.*,255,255, 0)", 0, 0, 0, 0, false }, + { "#\\d.*", 0, 0, 0, 0, false }, { "", 0, 0, 0, 0, false }, + { "rgba(\\d,\\d,\\d,0.0)", 0, 0, 0, 0, false }, + { "^rgba\\( \\.*)", 0, 0, 0, 0, false } }; + + ArrayList<Object[]> values = new ArrayList<>(); + Collections.addAll(values, validValues); + Collections.addAll(values, invalidValues); + + return values; + } + + @Test + public void testRGBAData() { + Matcher m = ColorUtil.RGBA_PATTERN.matcher(input); + boolean matches = m.matches(); + if (expectedMatches) { + Color expectedColor = new Color(expectedRed, expectedGreen, + expectedBlue, expectedAlpha); + Color c1 = ColorUtil.getRGBAPatternColor(m); + + assertTrue(expectedColor.equals(c1)); + } else { + assertTrue(!matches); + } + } +} diff --git a/server/src/test/java/com/vaadin/tests/server/component/colorpicker/RGBPatternParsingTest.java b/server/src/test/java/com/vaadin/tests/server/component/colorpicker/RGBPatternParsingTest.java new file mode 100644 index 0000000000..f853663faa --- /dev/null +++ b/server/src/test/java/com/vaadin/tests/server/component/colorpicker/RGBPatternParsingTest.java @@ -0,0 +1,87 @@ +package com.vaadin.tests.server.component.colorpicker; + +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.regex.Matcher; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +import com.vaadin.shared.ui.colorpicker.Color; +import com.vaadin.ui.components.colorpicker.ColorUtil; + +@RunWith(value = Parameterized.class) +public class RGBPatternParsingTest { + + @Parameter(value = 0) + public String input; + + @Parameter(value = 1) + public int expectedRed; + @Parameter(value = 2) + public int expectedGreen; + @Parameter(value = 3) + public int expectedBlue; + + @Parameter(value = 4) + public boolean expectedMatches; + + @Parameters(name = "{index}: testRGBData({0}) = ({1},{2},{3},{4})") + public static Collection<Object[]> rgbdata() { + Object[][] validValues = { { "rgb(0,0,0)", 0, 0, 0, true }, + { "rgb(0, 0, 0)", 0, 0, 0, true }, + { "rgb(0 0 0)", 0, 0, 0, true }, + { "rgb(1 1 1)", 1, 1, 1, true }, + { "rgb(0 100 255)", 0, 100, 255, true }, + { "rgb(255,255,255)", 255, 255, 255, true }, + { "RGB(255, 255, 255 )", 255, 255, 255, true }, + { "rgb(255 255 255)", 255, 255, 255, true }, + { "rgb(1, 10, 100)", 1, 10, 100, true } }; + Object[][] invalidValues = { { "rgb(,0,0)", 0, 0, 0, false }, + { "rgb(0, 0, 0, )", 0, 0, 0, false }, + { "rgb(0.0,0,0)", 0, 0, 0, false }, + { "rgb( 0 0 -1 )", 0, 0, 0, false }, + { "rgb(1 00)", 0, 0, 0, false }, + { "rgb(255,255,255.)", 0, 0, 0, false }, + { "r gb(255, 256, 255)", 0, 0, 0, false }, + { "rgb( 255, 255, 256 )", 0, 0, 0, false }, + { "rgb(163, 2%, 210)", 0, 0, 0, false }, + { "rGb(000)", 0, 0, 0, false }, + { "rgb(255255255)", 0, 0, 0, false }, + { "rGBA(255,255,255)", 0, 0, 0, false }, + { "rgb 255 255 255)", 0, 0, 0, false }, + { "255, 255, 0", 0, 0, 0, false }, + { "hsl(10,0,0)", 0, 0, 0, false }, + { "\\s%\\d[0-9]", 0, 0, 0, false }, + { "rgb(\\.*,255,255)", 0, 0, 0, false }, + { "#\\d.*", 0, 0, 0, false }, { "", 0, 0, 0, false }, + { "rgb(\\d,\\d,\\d)", 0, 0, 0, false }, + { "^rgb\\( \\.*)", 0, 0, 0, false } }; + + ArrayList<Object[]> values = new ArrayList<>(); + Collections.addAll(values, validValues); + Collections.addAll(values, invalidValues); + + return values; + } + + @Test + public void testRGBData() { + Matcher m = ColorUtil.RGB_PATTERN.matcher(input); + boolean matches = m.matches(); + if (expectedMatches) { + Color expectedColor = new Color(expectedRed, expectedGreen, + expectedBlue); + Color c1 = ColorUtil.getRGBPatternColor(m); + assertTrue(expectedColor.equals(c1)); + } else { + assertTrue(!matches); + } + } +} diff --git a/testbench-api/src/main/java/com/vaadin/testbench/elements/ColorPickerPreviewElement.java b/testbench-api/src/main/java/com/vaadin/testbench/elements/ColorPickerPreviewElement.java index 03f10ecc7c..d733e7a531 100644 --- a/testbench-api/src/main/java/com/vaadin/testbench/elements/ColorPickerPreviewElement.java +++ b/testbench-api/src/main/java/com/vaadin/testbench/elements/ColorPickerPreviewElement.java @@ -15,9 +15,66 @@ */ package com.vaadin.testbench.elements; +import java.util.List; + +import org.openqa.selenium.Keys; +import org.openqa.selenium.WebElement; + +import com.vaadin.testbench.By; import com.vaadin.testbench.elementsbase.ServerClass; -@ServerClass("com.vaadin.ui.ColorPickerPreview") +@ServerClass("com.vaadin.ui.components.colorpicker.ColorPickerPreview") public class ColorPickerPreviewElement extends CssLayoutElement { + /** + * Get whether TextField in ColorPickerPreview has validation errors. + * + * @return true if field has errors, false otherwise + * + * @since + */ + public boolean getColorFieldContainsErrors() { + List<WebElement> caption = findElements( + By.className("v-caption-v-colorpicker-preview-textfield")); + boolean noCaption = caption.isEmpty(); + return noCaption ? noCaption + : caption.get(0).findElements(By.className("v-errorindicator")) + .isEmpty(); + } + + /** + * Get the value of the input element TextField in ColorPickerPreview. + * + * @return the value of the attribute 'value' of the input element + * + * @since + */ + public String getColorFieldValue() { + return getColorTextField().getAttribute("value"); + } + + /** + * Set value of TextField in ColorPickerPreview. Any existing value in the + * field is replaced. + * + * @param value + * text to insert + * + * @since + */ + public void setColorTextFieldValue(String value) { + // Select all text + getColorTextField().sendKeys(Keys.chord(Keys.CONTROL, "a")); + getColorTextField().sendKeys(value); + } + + /** + * @return <code>WebElement</code> representing TextField in + * ColorPickerPreviewComponent + * + * @since + */ + public WebElement getColorTextField() { + return findElement(By.className("v-colorpicker-preview-textfield")); + } } diff --git a/themes/src/main/themes/VAADIN/themes/valo/components/_colorpicker.scss b/themes/src/main/themes/VAADIN/themes/valo/components/_colorpicker.scss index d6cd8ad093..c0b471d81f 100644 --- a/themes/src/main/themes/VAADIN/themes/valo/components/_colorpicker.scss +++ b/themes/src/main/themes/VAADIN/themes/valo/components/_colorpicker.scss @@ -189,6 +189,7 @@ width: 100% !important; height: auto !important; padding: round($v-unit-size/4); + display: inline-flex; } .#{$primary-stylename}-preview-textfield { diff --git a/uitest/src/main/java/com/vaadin/tests/components/colorpicker/ValoColorPickerTestUI.java b/uitest/src/main/java/com/vaadin/tests/components/colorpicker/ValoColorPickerTestUI.java new file mode 100644 index 0000000000..c0827cef05 --- /dev/null +++ b/uitest/src/main/java/com/vaadin/tests/components/colorpicker/ValoColorPickerTestUI.java @@ -0,0 +1,474 @@ +package com.vaadin.tests.components.colorpicker; + +import java.awt.Graphics; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.text.SimpleDateFormat; +import java.util.Date; + +import javax.imageio.ImageIO; + +import com.vaadin.annotations.Widgetset; +import com.vaadin.data.HasValue.ValueChangeEvent; +import com.vaadin.server.StreamResource; +import com.vaadin.server.VaadinRequest; +import com.vaadin.shared.ui.ContentMode; +import com.vaadin.shared.ui.colorpicker.Color; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.AbstractColorPicker; +import com.vaadin.ui.Alignment; +import com.vaadin.ui.CheckBox; +import com.vaadin.ui.ColorPicker; +import com.vaadin.ui.ColorPickerArea; +import com.vaadin.ui.Embedded; +import com.vaadin.ui.GridLayout; +import com.vaadin.ui.HorizontalLayout; +import com.vaadin.ui.Label; +import com.vaadin.ui.Panel; +import com.vaadin.ui.VerticalLayout; + +@Widgetset("com.vaadin.DefaultWidgetSet") +public class ValoColorPickerTestUI extends AbstractTestUI { + + @Override + public String getTestDescription() { + return "Vaadin 8 Valo theme ColorPicker"; + } + + @Override + protected Integer getTicketNumber() { + return 10802; + } + + /** The foreground color. */ + private Color foregroundColor = Color.BLACK; // The currently selected + + /** The background color. */ + private Color backgroundColor = Color.WHITE; // The currently selected + + // The display box where the image is rendered + /** The display. */ + private Embedded display; + + private AbstractColorPicker colorpicker1; + private AbstractColorPicker colorpicker2; + private AbstractColorPicker colorpicker3; + private AbstractColorPicker colorpicker4; + private AbstractColorPicker colorpicker5; + private AbstractColorPicker colorpicker6; + + private boolean rgbVisible = true; + private boolean hsvVisible = true; + private boolean swaVisible = true; + private boolean historyVisible = true; + private boolean txtfieldVisible = true; + + private final CheckBox rgbBox = new CheckBox("RGB tab visible"); + private final CheckBox hsvBox = new CheckBox("HSV tab visible"); + private final CheckBox swaBox = new CheckBox("Swatches tab visible"); + private final CheckBox hisBox = new CheckBox("History visible"); + private final CheckBox txtBox = new CheckBox("CSS field visible"); + + /** + * This class is used to represent the preview of the color selection. + */ + public class MyImageSource implements StreamResource.StreamSource { + + /** The imagebuffer. */ + private ByteArrayOutputStream imagebuffer; + + /** The bg color. */ + private final java.awt.Color bgColor; + + /** The fg color. */ + private final java.awt.Color fgColor; + + /** + * Instantiates a new my image source. + * + * @param fg + * the foreground + * @param bg + * the background + */ + public MyImageSource(java.awt.Color fg, java.awt.Color bg) { + fgColor = fg; + bgColor = bg; + } + + /* + * Must implement this method that returns the resource as a stream. + */ + @Override + public InputStream getStream() { + + /* Create an image and draw something on it. */ + BufferedImage image = new BufferedImage(270, 270, + BufferedImage.TYPE_INT_RGB); + Graphics drawable = image.getGraphics(); + drawable.setColor(bgColor); + drawable.fillRect(0, 0, 270, 270); + drawable.setColor(fgColor); + drawable.fillOval(25, 25, 220, 220); + drawable.setColor(java.awt.Color.blue); + drawable.drawRect(0, 0, 269, 269); + drawable.setColor(java.awt.Color.black); + drawable.drawString("r=" + String.valueOf(fgColor.getRed()) + ",g=" + + String.valueOf(fgColor.getGreen()) + ",b=" + + String.valueOf(fgColor.getBlue()), 50, 100); + drawable.drawString("r=" + String.valueOf(bgColor.getRed()) + ",g=" + + String.valueOf(bgColor.getGreen()) + ",b=" + + String.valueOf(bgColor.getBlue()), 5, 15); + + try { + /* Write the image to a buffer. */ + imagebuffer = new ByteArrayOutputStream(); + ImageIO.write(image, "png", imagebuffer); + + /* Return a stream from the buffer. */ + return new ByteArrayInputStream(imagebuffer.toByteArray()); + } catch (IOException e) { + return null; + } + } + } + + private void setPopupVisibilities() { + + rgbBox.setEnabled(!(rgbVisible && !hsvVisible && !swaVisible)); + hsvBox.setEnabled(!(!rgbVisible && hsvVisible && !swaVisible)); + swaBox.setEnabled(!(!rgbVisible && !hsvVisible && swaVisible)); + + colorpicker1.setRGBVisibility(rgbVisible); + colorpicker2.setRGBVisibility(rgbVisible); + colorpicker3.setRGBVisibility(rgbVisible); + colorpicker4.setRGBVisibility(rgbVisible); + colorpicker5.setRGBVisibility(rgbVisible); + colorpicker6.setRGBVisibility(rgbVisible); + + colorpicker1.setHSVVisibility(hsvVisible); + colorpicker2.setHSVVisibility(hsvVisible); + colorpicker3.setHSVVisibility(hsvVisible); + colorpicker4.setHSVVisibility(hsvVisible); + colorpicker5.setHSVVisibility(hsvVisible); + colorpicker6.setHSVVisibility(hsvVisible); + + colorpicker1.setSwatchesVisibility(swaVisible); + colorpicker2.setSwatchesVisibility(swaVisible); + colorpicker3.setSwatchesVisibility(swaVisible); + colorpicker4.setSwatchesVisibility(swaVisible); + colorpicker5.setSwatchesVisibility(swaVisible); + colorpicker6.setSwatchesVisibility(swaVisible); + + colorpicker1.setHistoryVisibility(historyVisible); + colorpicker2.setHistoryVisibility(historyVisible); + colorpicker3.setHistoryVisibility(historyVisible); + colorpicker4.setHistoryVisibility(historyVisible); + colorpicker5.setHistoryVisibility(historyVisible); + colorpicker6.setHistoryVisibility(historyVisible); + + colorpicker1.setTextfieldVisibility(txtfieldVisible); + colorpicker2.setTextfieldVisibility(txtfieldVisible); + colorpicker3.setTextfieldVisibility(txtfieldVisible); + colorpicker4.setTextfieldVisibility(txtfieldVisible); + colorpicker5.setTextfieldVisibility(txtfieldVisible); + colorpicker6.setTextfieldVisibility(txtfieldVisible); + } + + @Override + protected void setup(VaadinRequest request) { + getLayout().setWidth("1100px"); + getLayout().setHeight(null); + getLayout().addStyleName("colorpicker-mainwindow-content"); + + // Create an instance of the preview and add it to the window + display = new Embedded("Color preview"); + display.setWidth("270px"); + display.setHeight("270px"); + + // Add the foreground and background colorpickers to a layout + HorizontalLayout mainLayout = new HorizontalLayout(); + mainLayout.addStyleName("colorpicker-mainlayout"); + mainLayout.setWidth("100%"); + mainLayout.setHeight(null); + mainLayout.setMargin(true); + mainLayout.setSpacing(true); + getLayout().addComponent(mainLayout); + + VerticalLayout layoutLeft = new VerticalLayout(); + layoutLeft.setWidth("550px"); + layoutLeft.setHeight(null); + layoutLeft.setSpacing(true); + + GridLayout optLayout = new GridLayout(2, 3); + optLayout.setWidth("100%"); + optLayout.setHeight(null); + optLayout.setMargin(true); + optLayout.setSpacing(true); + + rgbBox.setValue(rgbVisible); + rgbBox.addValueChangeListener(event -> { + rgbVisible = event.getValue(); + setPopupVisibilities(); + }); + rgbBox.setId("rgbBox"); + optLayout.addComponent(rgbBox); + + hsvBox.setValue(hsvVisible); + hsvBox.addValueChangeListener(event -> { + hsvVisible = event.getValue(); + setPopupVisibilities(); + }); + hsvBox.setId("hsvBox"); + optLayout.addComponent(hsvBox); + + swaBox.setValue(swaVisible); + swaBox.addValueChangeListener(event -> { + swaVisible = event.getValue(); + setPopupVisibilities(); + }); + swaBox.setId("swaBox"); + optLayout.addComponent(swaBox); + + hisBox.setValue(historyVisible); + hisBox.addValueChangeListener(event -> { + historyVisible = event.getValue(); + setPopupVisibilities(); + }); + hisBox.setId("hisBox"); + optLayout.addComponent(hisBox); + + txtBox.setValue(txtfieldVisible); + txtBox.addValueChangeListener(event -> { + txtfieldVisible = event.getValue(); + setPopupVisibilities(); + }); + txtBox.setId("txtBox"); + optLayout.addComponent(txtBox); + + Panel optPanel = new Panel("Customize the color picker popup window", + optLayout); + layoutLeft.addComponent(optPanel); + + HorizontalLayout layout1 = createHorizontalLayout(); + + colorpicker1 = new ColorPicker("Foreground", foregroundColor); + colorpicker1.setCaptionAsHtml(true); + colorpicker1.addValueChangeListener(this::colorChanged); + colorpicker1.setId("colorpicker1"); + layout1.addComponent(colorpicker1); + layout1.setComponentAlignment(colorpicker1, Alignment.MIDDLE_CENTER); + + colorpicker2 = new ColorPicker("Background", backgroundColor); + colorpicker2.addValueChangeListener(this::colorChanged); + colorpicker2.setId("colorpicker2"); + layout1.addComponent(colorpicker2); + layout1.setComponentAlignment(colorpicker2, Alignment.MIDDLE_CENTER); + + Panel panel1 = new Panel( + "Button-like colorpicker with current color and CSS code", + layout1); + layoutLeft.addComponent(panel1); + + HorizontalLayout layout2 = createHorizontalLayout(); + + colorpicker3 = new ColorPicker("Foreground", foregroundColor); + colorpicker3.addValueChangeListener(this::colorChanged); + colorpicker3.setWidth("130px"); + colorpicker3.setCaption("Foreground"); + colorpicker3.setId("colorpicker3"); + layout2.addComponent(colorpicker3); + layout2.setComponentAlignment(colorpicker3, Alignment.MIDDLE_CENTER); + + colorpicker4 = new ColorPicker("Background", backgroundColor); + colorpicker4.addValueChangeListener(this::colorChanged); + colorpicker4.setWidth("130px"); + colorpicker4.setCaption("Background"); + colorpicker4.setId("colorpicker4"); + layout2.addComponent(colorpicker4); + layout2.setComponentAlignment(colorpicker4, Alignment.MIDDLE_CENTER); + + Panel panel2 = new Panel( + "Button-like colorpicker with current color and custom caption", + layout2); + layoutLeft.addComponent(panel2); + + HorizontalLayout layout3 = createHorizontalLayout(); + + colorpicker5 = new ColorPickerArea("Foreground", foregroundColor); + colorpicker5.setCaption("Foreground"); + colorpicker5.addValueChangeListener(this::colorChanged); + colorpicker5.setId("colorpicker5"); + layout3.addComponent(colorpicker5); + layout3.setComponentAlignment(colorpicker5, Alignment.MIDDLE_CENTER); + + colorpicker6 = new ColorPickerArea("Background", backgroundColor); + colorpicker6.setCaption("Background"); + colorpicker6.setDefaultCaptionEnabled(false); + colorpicker6.addValueChangeListener(this::colorChanged); + colorpicker6.setId("colorpicker6"); + layout3.addComponent(colorpicker6); + layout3.setComponentAlignment(colorpicker6, Alignment.MIDDLE_CENTER); + + Panel panel3 = new Panel("Color area colorpicker with caption", + layout3); + panel3.setWidth("100%"); + panel3.setHeight(null); + layoutLeft.addComponent(panel3); + + Label divider1 = new Label("<hr>", ContentMode.HTML); + layoutLeft.addComponent(divider1); + + Label divider2 = new Label("<hr>", ContentMode.HTML); + layoutLeft.addComponent(divider2); + + HorizontalLayout layout4 = createHorizontalLayout(); + + addShadeButton(new Color(Integer.parseInt("000000", 16)), layout4); + addShadeButton(new Color(Integer.parseInt("333333", 16)), layout4); + addShadeButton(new Color(Integer.parseInt("666666", 16)), layout4); + addShadeButton(new Color(Integer.parseInt("999999", 16)), layout4); + addShadeButton(new Color(Integer.parseInt("cccccc", 16)), layout4); + addShadeButton(new Color(Integer.parseInt("ffffff", 16)), layout4); + + Panel panel4 = new Panel( + "Button-like colorpickers with disabled caption (no effect on fg/bg colors)", + layout4); + layoutLeft.addComponent(panel4); + + HorizontalLayout layout5 = createHorizontalLayout(); + + addShadeArea(new Color(Integer.parseInt("000000", 16)), layout5); + addShadeArea(new Color(Integer.parseInt("111111", 16)), layout5); + addShadeArea(new Color(Integer.parseInt("222222", 16)), layout5); + addShadeArea(new Color(Integer.parseInt("333333", 16)), layout5); + addShadeArea(new Color(Integer.parseInt("444444", 16)), layout5); + addShadeArea(new Color(Integer.parseInt("555555", 16)), layout5); + addShadeArea(new Color(Integer.parseInt("666666", 16)), layout5); + addShadeArea(new Color(Integer.parseInt("777777", 16)), layout5); + addShadeArea(new Color(Integer.parseInt("888888", 16)), layout5); + addShadeArea(new Color(Integer.parseInt("999999", 16)), layout5); + addShadeArea(new Color(Integer.parseInt("aaaaaa", 16)), layout5); + addShadeArea(new Color(Integer.parseInt("bbbbbb", 16)), layout5); + addShadeArea(new Color(Integer.parseInt("cccccc", 16)), layout5); + addShadeArea(new Color(Integer.parseInt("dddddd", 16)), layout5); + addShadeArea(new Color(Integer.parseInt("eeeeee", 16)), layout5); + addShadeArea(new Color(Integer.parseInt("ffffff", 16)), layout5); + + Panel panel5 = new Panel( + "Area colorpickers with no given caption (no effect on fg/bg colors)", + layout5); + layoutLeft.addComponent(panel5); + + mainLayout.addComponent(layoutLeft); + + mainLayout.addComponent(display); + + updateDisplay(foregroundColor, backgroundColor); + } + + private HorizontalLayout createHorizontalLayout() { + HorizontalLayout layout = new HorizontalLayout(); + layout.setWidth("100%"); + layout.setHeight(null); + layout.setMargin(true); + return layout; + } + + private int shadeButtonCounter = 1; + + private void addShadeButton(Color color, HorizontalLayout layout) { + AbstractColorPicker colorPicker = new ColorPicker(color.toString(), + color); + colorPicker.setDefaultCaptionEnabled(false); + colorPicker.setWidth("41px"); + colorPicker.setId("shadebutton_" + shadeButtonCounter); + layout.addComponent(colorPicker); + layout.setComponentAlignment(colorPicker, Alignment.MIDDLE_CENTER); + + ++shadeButtonCounter; + } + + private int shadeAreaCounter = 1; + + private void addShadeArea(Color color, HorizontalLayout layout) { + AbstractColorPicker colorPicker = new ColorPickerArea(color.toString(), + color); + colorPicker.setWidth("20px"); + colorPicker.setHeight("20px"); + colorPicker.setId("shadearea_" + shadeAreaCounter); + layout.addComponent(colorPicker); + layout.setComponentAlignment(colorPicker, Alignment.MIDDLE_CENTER); + + ++shadeAreaCounter; + } + + // This is called whenever a colorpicker popup is closed + /** + * Update display. + * + * @param fg + * the fg + * @param bg + * the bg + */ + public void updateDisplay(Color fg, Color bg) { + java.awt.Color awtFg = new java.awt.Color(fg.getRed(), fg.getGreen(), + fg.getBlue()); + java.awt.Color awtBg = new java.awt.Color(bg.getRed(), bg.getGreen(), + bg.getBlue()); + StreamResource.StreamSource imagesource = new MyImageSource(awtFg, + awtBg); + + Date now = new Date(); + SimpleDateFormat format = new SimpleDateFormat("hhmmss"); + + StreamResource imageresource = new StreamResource(imagesource, + "myimage" + format.format(now) + ".png"); + imageresource.setCacheTime(0); + + display.setSource(imageresource); + } + + private void colorChanged(ValueChangeEvent<Color> event) { + if (event.getSource() == colorpicker1 + || event.getSource() == colorpicker3 + || event.getSource() == colorpicker5) { + foregroundColor = event.getValue(); + + if (event.getSource() != colorpicker1) { + colorpicker1.setValue(event.getValue()); + } + if (event.getSource() != colorpicker3) { + colorpicker3.setValue(event.getValue()); + } + if (event.getSource() != colorpicker5) { + colorpicker5.setValue(event.getValue()); + } + + } else if (event.getSource() == colorpicker2 + || event.getSource() == colorpicker4 + || event.getSource() == colorpicker6) { + backgroundColor = event.getValue(); + + if (event.getSource() != colorpicker2) { + colorpicker2.setValue(event.getValue()); + } + if (event.getSource() != colorpicker4) { + colorpicker4.setValue(event.getValue()); + } + if (event.getSource() != colorpicker6) { + colorpicker6.setValue(event.getValue()); + } + + } else { + return; + } + + updateDisplay(foregroundColor, backgroundColor); + } + +} diff --git a/uitest/src/test/java/com/vaadin/tests/components/colorpicker/ValoColorPickerInputFormatsTest.java b/uitest/src/test/java/com/vaadin/tests/components/colorpicker/ValoColorPickerInputFormatsTest.java new file mode 100644 index 0000000000..e2dda5a87e --- /dev/null +++ b/uitest/src/test/java/com/vaadin/tests/components/colorpicker/ValoColorPickerInputFormatsTest.java @@ -0,0 +1,166 @@ +package com.vaadin.tests.components.colorpicker; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.List; + +import org.junit.Before; +import org.junit.Test; +import org.openqa.selenium.Keys; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.remote.DesiredCapabilities; + +import com.vaadin.testbench.elements.ColorPickerElement; +import com.vaadin.testbench.elements.ColorPickerPreviewElement; +import com.vaadin.testbench.parallel.Browser; +import com.vaadin.tests.tb3.MultiBrowserTest; + +/** + * Test legal color values according to + * http://www.w3schools.com/cssref/css_colors_legal.asp + */ +public class ValoColorPickerInputFormatsTest extends MultiBrowserTest { + + @Override + public List<DesiredCapabilities> getBrowsersToTest() { + // Ignoring Phantom JS + return getBrowserCapabilities(Browser.IE11, Browser.FIREFOX, + Browser.CHROME); + } + + private ColorPickerPreviewElement previewElement; + + @Before + public void setUp() { + openTestURL(); + getPreviewElement(); + } + + private void getPreviewElement() { + ColorPickerElement cpElement = $(ColorPickerElement.class) + .id("colorpicker1"); + // Open ColorPicker + cpElement.click(); + // Find preview element + previewElement = $(ColorPickerPreviewElement.class).first(); + } + + @Override + protected Class<?> getUIClass() { + return ValoColorPickerTestUI.class; + } + + @Test + public void testRGBValue() throws Exception { + setColorpickerValue("rgb(100 100 100)"); + + assertEquals("#646464", previewElement.getColorFieldValue()); + } + + @Test + public void testRGBAValue() { + setColorpickerValue("rgba(100,100,100, 0.5)"); + + assertEquals("#646464", previewElement.getColorFieldValue()); + } + + @Test + public void testHSLValue() { + setColorpickerValue("hsl(120,100%, 50%)"); + + assertEquals("#00ff00", previewElement.getColorFieldValue()); + } + + @Test + public void testHSLAValue() { + setColorpickerValue("hsla(120, 0, 50%, 0.3)"); + + assertEquals("#00ff00", previewElement.getColorFieldValue()); + } + + @Test + public void testHexTextInputValidation() { + // set valid hex value to ColorTextField + setColorpickerValue("#AAbb33"); + assertFalse(previewElement.getColorFieldContainsErrors()); + } + + @Test + public void testRGBTextInputValidation() { + String rgbString = "rgb(255 10 0)"; + // set valid rgb value to ColorTextField + setColorpickerValue(rgbString); + assertFalse(previewElement.getColorFieldContainsErrors()); + } + + @Test + public void testHSLTextInputValidation() { + String hslString = "HSL(300, 60, 100)"; + setColorpickerValue(hslString); + assertFalse(previewElement.getColorFieldContainsErrors()); + } + + @Test + public void testHexTextInputValidationError() { + // set invalid hex value to ColorTextField + setColorpickerValue("#xyz"); + assertTrue(previewElement.getColorFieldContainsErrors()); + } + + @Test + public void testRGBTextInputValidationError() { + String rgbString = "rgb(300, 60, 90)"; + // set invalid rgb value to ColorTextField + setColorpickerValue(rgbString); + assertTrue(previewElement.getColorFieldContainsErrors()); + } + + @Test + public void testRGBATextInputValidationError() { + String rgbaString = "rgba(250, 0, 10, 6.0)"; + // set invalid rgba value to ColorTextField + setColorpickerValue(rgbaString); + assertTrue(previewElement.getColorFieldContainsErrors()); + } + + @Test + public void testHSLTextInputValidationError() { + String hslString = "hsl(370,60%,120%)"; + // set invalid hsl value to ColorTextField + setColorpickerValue(hslString); + assertTrue(previewElement.getColorFieldContainsErrors()); + } + + @Test + public void testHSLATextInputValidationError() { + String hslaString = "hsla(300, 50, 10, 1.1)"; + // set invalid hsla value to ColorTextField + setColorpickerValue(hslaString); + assertTrue(previewElement.getColorFieldContainsErrors()); + } + + @Test + public void testFailedValidationResult() { + // set invalid hex value to ColorTextField + setColorpickerValue("#xyz"); + // verify there are errors + assertTrue(previewElement.getColorFieldContainsErrors()); + // verify value has not been changed + assertEquals(previewElement.getColorFieldValue(), "#xyz"); + } + + private void setColorpickerValue(String value) { + WebElement field = previewElement.getColorTextField(); + + // Select all text + field.sendKeys(Keys.chord(Keys.CONTROL, "a")); + + // Replace with new value + field.sendKeys(value); + + // Submit + field.sendKeys(Keys.RETURN); + } +} |