diff options
5 files changed, 797 insertions, 0 deletions
diff --git a/WebContent/VAADIN/themes/tests-responsive/styles.css b/WebContent/VAADIN/themes/tests-responsive/styles.css new file mode 100644 index 0000000000..4bfc6dd768 --- /dev/null +++ b/WebContent/VAADIN/themes/tests-responsive/styles.css @@ -0,0 +1,92 @@ +@import url(../reindeer/legacy-styles.css); + +.grid { + overflow: visible; +} + +/* Style the items in the grid */ +.grid > .v-label { + outline: 1px dotted rgba(0, 0, 0, .3); + text-align: center; + padding: 2em 0; + font-size: 16px; + font-weight: bold; +} + +/* We want the items in the grid to stay between 100px and 200px, if possible */ +.first[width-range~="-200px"] .v-label { + width: 100%; +} + +.first[width-range~="201px-400px"] > .v-label { + width: 50%; +} + +.first[width-range~="401px-600px"] > .v-label { + width: 33.3333%; +} + +.first[width-range~="601-800"] > .v-label { + width: 25%; +} + +.first[width-range~="801-1000px"] > .v-label { + width: 20%; +} + +.first[width-range~="1001px-"] > .v-label { + width: 16.6667%; +} + +.second[width-range~="0-100px"] .v-label { + width: 100%; +} + +.second[width-range~="101px-200px"] > .v-label { + width: 50%; +} + +.second[width-range~="201px-300px"] > .v-label { + width: 33.3333%; +} + +.second[width-range~="301px-400px"] > .v-label { + width: 25%; +} + +.second[width-range~="401px-500px"] > .v-label { + width: 20%; +} + +.second[width-range~="501px-"] > .v-label { + width: 16.6667%; +} + +.description { + padding: 1em; + line-height: 1.5; +} + +.description[width-range="-100px"] { + font-size: 10px; +} + +.description[width-range="101px-200px"] { + font-size: 12px; +} + +.description[width-range="200px-350px"] { + font-size: 14px; +} + +.description[width-range="351px-500px"] { + font-size: 18px; +} + +.description[width-range="501px-700px"] { + font-size: 24px; +} + +.description[width-range="701px-"] { + font-size: 30px; +} diff --git a/client/src/com/vaadin/client/extensions/ResponsiveConnector.java b/client/src/com/vaadin/client/extensions/ResponsiveConnector.java new file mode 100644 index 0000000000..fb1919e6c2 --- /dev/null +++ b/client/src/com/vaadin/client/extensions/ResponsiveConnector.java @@ -0,0 +1,379 @@ +/* + * Copyright 2000-2013 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.client.extensions; + +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.google.gwt.core.client.JavaScriptObject; +import com.google.gwt.dom.client.Element; +import com.vaadin.client.LayoutManager; +import com.vaadin.client.ServerConnector; +import com.vaadin.client.ui.AbstractComponentConnector; +import com.vaadin.client.ui.layout.ElementResizeEvent; +import com.vaadin.client.ui.layout.ElementResizeListener; +import com.vaadin.server.Responsive; +import com.vaadin.shared.ui.Connect; + +/** + * The client side connector for the Responsive extension. + * + * @author Vaadin Ltd + * @since 7.2 + */ +@SuppressWarnings("GwtInconsistentSerializableClass") +@Connect(Responsive.class) +public class ResponsiveConnector extends AbstractExtensionConnector implements + ElementResizeListener { + + /** + * The target component which we will monitor for width changes + */ + protected AbstractComponentConnector target; + + /** + * All the width breakpoints found for this particular instance + */ + protected JavaScriptObject widthBreakpoints; + + /** + * All the height breakpoints found for this particular instance + */ + protected JavaScriptObject heightBreakpoints; + + /** + * All width-range breakpoints found from the style sheets on the page. + * Common for all instances. + */ + protected static JavaScriptObject widthRangeCache; + + /** + * All height-range breakpoints found from the style sheets on the page. + * Common for all instances. + */ + protected static JavaScriptObject heightRangeCache; + + private static Logger getLogger() { + return Logger.getLogger(ResponsiveConnector.class.getName()); + } + + private static void error(String message) { + getLogger().log(Level.SEVERE, message); + } + + @Override + protected void extend(ServerConnector target) { + // Initialize cache if not already done + if (widthRangeCache == null) { + searchForBreakPoints(); + } + + this.target = (AbstractComponentConnector) target; + + // Get any breakpoints from the styles defined for this widget + getBreakPointsFor(constructSelectorsForTarget()); + + // Start listening for size changes + LayoutManager.get(getConnection()).addElementResizeListener( + this.target.getWidget().getElement(), this); + } + + /** + * Construct the list of selectors that should be matched against in the + * range selectors + * + * @return The selectors in a comma delimited string. + */ + protected String constructSelectorsForTarget() { + String primaryStyle = this.target.getState().primaryStyleName; + StringBuilder selectors = new StringBuilder(); + selectors.append(".").append(primaryStyle); + + if (this.target.getState().styles != null + && this.target.getState().styles.size() > 0) { + for (String style : this.target.getState().styles) { + selectors.append(",.").append(style); + selectors.append(",.").append(primaryStyle).append(".") + .append(style); + selectors.append(",.").append(style).append(".") + .append(primaryStyle); + selectors.append(",.").append(primaryStyle).append("-") + .append(style); + } + } + + // Allow the ID to be used as the selector as well for ranges + if (this.target.getState().id != null) { + selectors.append(",#").append(this.target.getState().id); + } + return selectors.toString(); + } + + @Override + public void onUnregister() { + super.onUnregister(); + LayoutManager.get(getConnection()).removeElementResizeListener( + this.target.getWidget().getElement(), this); + } + + /** + * Build a cache of all 'width-range' and 'height-range' attribute selectors + * found in the stylesheets. + */ + private static native void searchForBreakPoints() + /*-{ + + // Initialize variables + @com.vaadin.client.extensions.ResponsiveConnector::widthRangeCache = []; + @com.vaadin.client.extensions.ResponsiveConnector::heightRangeCache = []; + + var widthRanges = @com.vaadin.client.extensions.ResponsiveConnector::widthRangeCache; + var heightRanges = @com.vaadin.client.extensions.ResponsiveConnector::heightRangeCache; + + // Can't do squat if we can't parse stylesheets + if(!$doc.styleSheets) + return; + + var sheets = $doc.styleSheets; + + // Loop all stylesheets on the page and process them individually + for(var i = 0, len = sheets.length; i < len; i++) { + var sheet = sheets[i]; + @com.vaadin.client.extensions.ResponsiveConnector::searchStylesheetForBreakPoints(Lcom/google/gwt/core/client/JavaScriptObject;)(sheet); + } + + }-*/; + + /** + * Process an individual stylesheet object. Any @import statements are + * handled recursively. Regular rule declarations are searched for + * 'width-range' and 'height-range' attribute selectors. + * + * @param sheet + */ + private static native void searchStylesheetForBreakPoints( + final JavaScriptObject sheet) + /*-{ + + // Inline variables for easier reading + var widthRanges = @com.vaadin.client.extensions.ResponsiveConnector::widthRangeCache; + var heightRanges = @com.vaadin.client.extensions.ResponsiveConnector::heightRangeCache; + + // Get all the rulesets from the stylesheet + var theRules = new Array(); + var IE = @com.vaadin.client.BrowserInfo::get()().@com.vaadin.client.BrowserInfo::isIE()(); + var IE8 = @com.vaadin.client.BrowserInfo::get()().@com.vaadin.client.BrowserInfo::isIE8()(); + + if (sheet.cssRules) { + theRules = sheet.cssRules + } else if (sheet.rules) { + theRules = sheet.rules + } + + // Special import handling for IE8 + if (IE8) { + try { + for(var i = 0, len = sheet.imports.length; i < len; i++) { + @com.vaadin.client.extensions.ResponsiveConnector::searchStylesheetForBreakPoints(Lcom/google/gwt/core/client/JavaScriptObject;)(sheet.imports[i]); + } + } catch(e) { + // This is added due to IE8 failing to handle imports of some sheets for unknown reason (throws a permission denied exception) + @com.vaadin.client.extensions.ResponsiveConnector::error(Ljava/lang/String;)("Failed to handle imports of CSS style sheet: " + sheet.href); + } + } + + // Loop through the rulesets + for(var i = 0, len = theRules.length; i < len; i++) { + var rule = theRules[i]; + + if(rule.type == 3) { + // @import rule, traverse recursively + @com.vaadin.client.extensions.ResponsiveConnector::searchStylesheetForBreakPoints(Lcom/google/gwt/core/client/JavaScriptObject;)(rule.styleSheet); + + } else if(rule.type == 1 || !rule.type) { + // Regular selector rule + + // IE parses CSS like .class[attr="val"] into [attr="val"].class so we need to check for both + + // Pattern for matching [width-range] selectors + var widths = IE? /\[width-range~?=["|'](.*)-(.*)["|']\]([\.|#]\S+)/i : /([\.|#]\S+)\[width-range~?=["|'](.*)-(.*)["|']\]/i; + + // Patter for matching [height-range] selectors + var heights = IE? /\[height-range~?=["|'](.*)-(.*)["|']\]([\.|#]\S+)/i : /([\.|#]\S+)\[height-range~?=["|'](.*)-(.*)["|']\]/i; + + // Array of all of the separate selectors in this ruleset + var haystack = rule.selectorText.split(","); + + // Loop all the selectors in this ruleset + for(var k = 0, len2 = haystack.length; k < len2; k++) { + var result; + + // Check for width-range matches + if(result = haystack[k].match(widths)) { + var selector = IE? result[3] : result[1] + var min = IE? result[1] : result[2]; + var max = IE? result[2] : result[3]; + + // Avoid adding duplicates + var duplicate = false; + for(var l = 0, len3 = widthRanges.length; l < len3; l++) { + var bp = widthRanges[l]; + if(selector == bp[0] && min == bp[1] && max == bp[2]) { + duplicate = true; + break; + } + } + if(!duplicate) { + widthRanges.push([selector, min, max]); + } + } + + // Check for height-range matches + if(result = haystack[k].match(heights)) { + var selector = IE? result[3] : result[1] + var min = IE? result[1] : result[2]; + var max = IE? result[2] : result[3]; + + // Avoid adding duplicates + var duplicate = false; + for(var l = 0, len3 = heightRanges.length; l < len3; l++) { + var bp = heightRanges[l]; + if(selector == bp[0] && min == bp[1] && max == bp[2]) { + duplicate = true; + break; + } + } + if(!duplicate) { + heightRanges.push([selector, min, max]); + } + } + } + } + } + + }-*/; + + /** + * Get all matching ranges from the cache for this particular instance. + * + * @param selectors + */ + private native void getBreakPointsFor(final String selectors) + /*-{ + + var selectors = selectors.split(","); + + var widthBreakpoints = this.@com.vaadin.client.extensions.ResponsiveConnector::widthBreakpoints = []; + var heightBreakpoints = this.@com.vaadin.client.extensions.ResponsiveConnector::heightBreakpoints = []; + + var widthRanges = @com.vaadin.client.extensions.ResponsiveConnector::widthRangeCache; + var heightRanges = @com.vaadin.client.extensions.ResponsiveConnector::heightRangeCache; + + for(var i = 0, len = widthRanges.length; i < len; i++) { + var bp = widthRanges[i]; + for(var j = 0, len2 = selectors.length; j < len2; j++) { + if(bp[0] == selectors[j]) + widthBreakpoints.push(bp); + } + } + + for(var i = 0, len = heightRanges.length; i < len; i++) { + var bp = heightRanges[i]; + for(var j = 0, len2 = selectors.length; j < len2; j++) { + if(bp[0] == selectors[j]) + heightBreakpoints.push(bp); + } + } + + // Only for debugging + // console.log("Breakpoints for", selectors.join(","), widthBreakpoints, heightBreakpoints); + + }-*/; + + private String currentWidthRanges; + private String currentHeightRanges; + + @Override + public void onElementResize(ElementResizeEvent e) { + int width = e.getLayoutManager().getOuterWidth(e.getElement()); + int height = e.getLayoutManager().getOuterHeight(e.getElement()); + + // Loop through breakpoints and see which one applies to this width + currentWidthRanges = resolveBreakpoint("width", width, e.getElement()); + + if (!"".equals(currentWidthRanges)) { + this.target.getWidget().getElement() + .setAttribute("width-range", currentWidthRanges); + } else { + this.target.getWidget().getElement().removeAttribute("width-range"); + } + + // Loop through breakpoints and see which one applies to this height + currentHeightRanges = resolveBreakpoint("height", height, + e.getElement()); + + if (!"".equals(currentHeightRanges)) { + this.target.getWidget().getElement() + .setAttribute("height-range", currentHeightRanges); + } else { + this.target.getWidget().getElement() + .removeAttribute("height-range"); + } + } + + private native String resolveBreakpoint(String which, int size, + Element element) + /*-{ + + // Default to "width" breakpoints + var breakpoints = this.@com.vaadin.client.extensions.ResponsiveConnector::widthBreakpoints; + + // Use height breakpoints if we're measuring the height + if(which == "height") + breakpoints = this.@com.vaadin.client.extensions.ResponsiveConnector::heightBreakpoints; + + // Output string that goes into either the "width-range" or "height-range" attribute in the element + var ranges = ""; + + // Loop the breakpoints + for(var i = 0, len = breakpoints.length; i < len; i++) { + var bp = breakpoints[i]; + + var min = parseInt(bp[1]); + var max = parseInt(bp[2]); + + if(min && max) { + if(min <= size && size <= max) { + ranges += " " + bp[1] + "-" + bp[2]; + } + } else if (min) { + if(min <= size) { + ranges += " " + bp[1] + "-"; + } + } else if (max) { + if (size <= max) { + ranges += " -" + bp[2]; + } + } + } + + // Trim the output and return it + return ranges.replace(/^\s+/, ""); + + }-*/; + +} diff --git a/server/src/com/vaadin/server/Responsive.java b/server/src/com/vaadin/server/Responsive.java new file mode 100644 index 0000000000..d69c204c94 --- /dev/null +++ b/server/src/com/vaadin/server/Responsive.java @@ -0,0 +1,161 @@ +/* + * Copyright 2000-2013 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.server; + +import com.vaadin.ui.Component; + +/** + * An extension providing responsive layout capabilities to any Vaadin + * component. The Responsive extension allows specifying different CSS rules for + * different dimensions of extended components. This allows creating + * applications that provide an optimal viewing experience – easy reading and + * navigation with a minimum of resizing, panning, and scrolling – across a wide + * range of devices (from mobile phones to desktop computer monitors). + * <p> + * NOTE! You should always specify a relative (%) size for the extended + * component, doing otherwise will prevent the Responsive extension from + * working, as the component will not dynamically resize. + * </p> + * <p> + * All configuration of the visual breakpoints (ranges) for the component are + * done with CSS. Pixels (px) are the only supported unit. Fractional pixels are + * not supported. + * </p> + * <p> + * <i>Dynamic style injections (e.g. through + * <code>Page.getCurrent().getStyles().add(...)</code>) or any other style + * updates after the initial page load are not supported at the moment.</i> + * </p> + * + * + * <p> + * Example: + * + * <b>Java</b> + * + * <pre> + * CssLayout layout = new CssLayout(); + * layout.setStyleName("responsive"); + * layout.setSizeFull(); + * Responsive.makeResponsive(layout); + * </pre> + * + * <b>SCSS</b> + * + * <pre> + * .v-csslayout.responsive { + * &[width-range~="0-300px"] { + * // Styles for the layout when its width is between 0 and 300 pixels + * } + * &[width-range~="301-500px"] { + * // Styles for the layout when its width is between 301 and 500 pixels + * } + * &[width-range~="501px-"] { + * // Styles for the layout when its width is over 500 pixels + * } + * &[height-range~="0-300px"] { + * // Styles for the layout when its height is between 0 and 300 pixels + * } + * &[height-range~="301-500px"] { + * // Styles for the layout when its height is between 301 and 500 pixels + * } + * &[height-range~="501-"] { + * // Styles for the layout when its height is over 500 pixels + * } + * } + * </pre> + * + * <b>CSS</b> + * + * <pre> + * .v-csslayout.responsive[width-range~="0-300px"] { + * // Styles for the layout when its width is between 0 and 300 pixels + * } + * .v-csslayout.responsive[width-range~="301-500px"] { + * // Styles for the layout when its width is between 301 and 500 pixels + * } + * .v-csslayout.responsive[width-range~="501-"] { + * // Styles for the layout when its width is over 500 pixels + * } + * + * .v-csslayout.responsive[height-range~="0-300px"] { + * // Styles for the layout when its height is between 0 and 300 pixels + * } + * .v-csslayout.responsive[height-range~="301-500px"] { + * // Styles for the layout when its height is between 301 and 500 pixels + * } + * .v-csslayout.responsive[height-range~="501px-"] { + * // Styles for the layout when its height is over 500 pixels + * } + * </pre> + * + * </p> + * <p> + * <b>Note:</b> <i>The defined ranges are applied on a global context, so even + * if you would write your CSS to target only a given context, the ranges would + * be applied to all other instances with the same style name.</i> + * </p> + * <p> + * E.g. this would affect all CssLayout instances in the application, even + * though the CSS implies it would only affect CssLayout instances inside a + * parent with a style name "foobar": + * </p> + * + * <pre> + * .foobar .v-csslayout[width-range~="0px-100px"] { + * // These properties will affect all responsive CssLayout instances + * } + * </pre> + * + * <p> + * To scope the ranges, use an additional style name for the target component, + * and add that to your CSS selector: + * </p> + * + * <pre> + * .v-csslayout.mystyle[width-range="0px-100px"] { + * // These properties will only affect responsive CssLayout instances with an additional style name of 'mystyle' + * } + * </pre> + * + * @author Vaadin Ltd + * @since 7.2 + */ +public class Responsive extends AbstractExtension { + + /** + * Creates a new instance, which can be used to extend a component. + */ + protected Responsive() { + } + + /** + * Enable responsive width and height range styling for the target component + * or UI instance. + * + * @param target + * The component which should be able to respond to width and/or + * height changes. + */ + public static void makeResponsive(Component... components) { + for (Component c : components) { + if (c instanceof AbstractClientConnector) { + new Responsive().extend((AbstractClientConnector) c); + } + } + } +} diff --git a/uitest/src/com/vaadin/tests/extensions/ResponsiveUI.java b/uitest/src/com/vaadin/tests/extensions/ResponsiveUI.java new file mode 100644 index 0000000000..417821f1ea --- /dev/null +++ b/uitest/src/com/vaadin/tests/extensions/ResponsiveUI.java @@ -0,0 +1,87 @@ +/* + * Copyright 2000-2013 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.tests.extensions; + +import com.vaadin.annotations.Theme; +import com.vaadin.server.Responsive; +import com.vaadin.server.VaadinRequest; +import com.vaadin.shared.ui.label.ContentMode; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.CssLayout; +import com.vaadin.ui.HorizontalSplitPanel; +import com.vaadin.ui.Label; + +@Theme("tests-responsive") +public class ResponsiveUI extends AbstractTestUI { + @Override + protected void setup(VaadinRequest request) { + HorizontalSplitPanel split = new HorizontalSplitPanel(); + setContent(split); + split.setSplitPosition(50, Unit.PERCENTAGE); + split.setMinSplitPosition(100, Unit.PIXELS); + split.setMaxSplitPosition(1200, Unit.PIXELS); + setStyleName("responsive-test"); + + CssLayout firstGrid = makeGrid("first"); + CssLayout secondGrid = makeGrid("second"); + CssLayout grids = new CssLayout(); + grids.setSizeFull(); + grids.addComponent(firstGrid); + grids.addComponent(secondGrid); + split.addComponent(grids); + + Label description = new Label( + "<h3>This application demonstrates the Responsive extension in Vaadin.</h3>" + + "<p>Drag the splitter to see how the boxes on the left side adapt to " + + "different widths. They maintain a width of 100-200px, and always " + + "span the entire width of the container.</p><p>This label will " + + "adapt its font size and line height for different widths.</p>" + + "<p><a href=\"http://vaadin.com/download\">Download " + + "Vaadin</a></p>", ContentMode.HTML); + description.addStyleName("description"); + split.addComponent(description); + + // Add the responsive capabilities to the components + Responsive.makeResponsive(firstGrid); + Responsive.makeResponsive(secondGrid); + Responsive.makeResponsive(description); + } + + private CssLayout makeGrid(String styleName) { + CssLayout grid = new CssLayout(); + grid.setWidth("100%"); + grid.addStyleName("grid"); + grid.addStyleName(styleName); + + for (int i = 1; i < 10; i++) { + Label l = new Label("" + i); + l.setSizeUndefined(); + grid.addComponent(l); + } + return grid; + } + + @Override + protected String getTestDescription() { + return "The CssLayouts (grids) and Label should be responsive"; + } + + @Override + protected Integer getTicketNumber() { + return 12394; + } +} diff --git a/uitest/src/com/vaadin/tests/extensions/ResponsiveUITest.java b/uitest/src/com/vaadin/tests/extensions/ResponsiveUITest.java new file mode 100644 index 0000000000..76babed333 --- /dev/null +++ b/uitest/src/com/vaadin/tests/extensions/ResponsiveUITest.java @@ -0,0 +1,78 @@ +/* + * Copyright 2000-2013 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.tests.extensions; + +import static org.junit.Assert.assertEquals; + +import org.junit.Before; +import org.junit.Test; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.interactions.Actions; + +import com.vaadin.testbench.By; +import com.vaadin.tests.tb3.MultiBrowserTest; + +public class ResponsiveUITest extends MultiBrowserTest { + + @Before + public void setUp() throws Exception { + // We need this in order to ensure that the initial width-range is + // 401px-600px + testBench().resizeViewPortTo(1024, 768); + } + + // JQuery style selector + private WebElement $(String cssSelector) { + return getDriver().findElement(By.cssSelector(cssSelector)); + } + + @Test + public void testResizingSplitPanelReflowsLayout() throws Exception { + openTestURL(); + + assertEquals("401px-600px", + $(".v-csslayout-grid.first").getAttribute("width-range")); + assertEquals("501px-", + $(".v-csslayout-grid.second").getAttribute("width-range")); + + moveSplitter(200); + + assertEquals("601-800", + $(".v-csslayout-grid.first").getAttribute("width-range")); + assertEquals("501px-", + $(".v-csslayout-grid.second").getAttribute("width-range")); + + moveSplitter(-350); + + assertEquals("201px-400px", + $(".v-csslayout-grid.first").getAttribute("width-range")); + assertEquals("301px-400px", + $(".v-csslayout-grid.second").getAttribute("width-range")); + + compareScreen("responsive"); + + moveSplitter(-200); + assertEquals("-200px", + $(".v-csslayout-grid.first").getAttribute("width-range")); + + } + + private void moveSplitter(int xOffset) { + new Actions(getDriver()).clickAndHold($(".v-splitpanel-hsplitter")) + .moveByOffset(xOffset, 0).release().build().perform(); + } +} |